策略者模式的特点#
在设计类的继承体系时,我们会刻意的把公共的部分都提取到基类中
比如先设计Person类,把人类都具有的行为放到这个Person,特有的行为设计成抽象方法,让子类具体去实现, 这样后续无论我们再去构造学生,还是构造老师,大家都继承Person,就达到了代码复用的目的
但是这样问题就来了,对老师类来说,需要有教学的行为,假如这个方法以抽象方法的形式放在基类,那么对于继承了Person的学生类来说就不对了,因为没有要求学生一定会教学,但是现在学生就得实现这个方法
如果我们把老师的教学的行为作为 老师类的私有, 这时候,小明教小李学习, 就意味着对小明来说,他需要教学的行为, 前前后后看起来就开始矛盾了, 到底怎么处理呢?
策略者模式,就解决了这个问题, 它把行为抽象成了接口,以接口+实现的方式,解决上面的问题, 就上面的例子来说,可以把教学设计成接口,任何类,只要实现了这个接口,就可以教学,而不一定强制要求只有老师才可以实现它
总的来说,策略模式,就是将行为抽象成接口+实现的模式
Netty中策略者模式的使用#
netty的bossgroup
中接收到了新的连接之后会使用选择器Chooser
,从WorkerGroup
中选择出一个EventLoop
, 然后把这个连接注册进选出的 EventLoop
netty的选择器,使用的就是策略者模式,将选择的行为 设计成接口,不同的选择器根据自己不同的需求用不用的方式实现选择器接口
行为接口
@UnstableApi public interface EventExecutorChooserFactory { EventExecutorChooser newChooser(EventExecutor[] executors); @UnstableApi interface EventExecutorChooser { EventExecutor next(); } }
选择器不同的实现:
if (isPowerOfTwo(executors.length)) {// todo 如果是2的指数倍, 返回PowerOfTwoEventExecutorChooser return new PowerOfTwoEventExecutorChooser(executors); } else {// todo 否则返回同样的实例 return new GenericEventExecutorChooser(executors); }
根据线程执行器的数量确定使用那种具体的行为
行为1:PowerOfTwoEventExecutorChooser
#
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; PowerOfTwoEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[idx.getAndIncrement() & executors.length - 1]; }
主要看它的executors[idx.getAndIncrement() & executors.length - 1]
进行速度更快的与运算
1 & 1 = 1 1 & 0 = 0 0 & 1 = 0
当数组的长度是2的幂次方时, 用二进制表示就是1111... 全是1, 再减去1 ,就是0111...
无论前面的数是谁,对一个 0111... 进行与运算,得到的结果就是从0-0111...大小的数, 循环往复
行为2:GenericEventExecutorChooser
#
private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { // todo 从0开始到最后一个, 再从零开始,到最后一个 return executors[Math.abs(idx.getAndIncrement() % executors.length)]; }
主要的一步就是Math.abs(idx.getAndIncrement() % executors.length)
可以看到,从0开始一直往后对数组的长度取余数,小数对大数取余数=小数, 保证了数组的下标从0开始递增, 自己对自己取余数=0,保证了最大值是 数组的长度减一, 如此往复