在设计类的继承体系时,我们会刻意的把公共的部分都提取到基类中

比如先设计Person类,把人类都具有的行为放到这个Person,特有的行为设计成抽象方法,让子类具体去实现, 这样后续无论我们再去构造学生,还是构造老师,大家都继承Person,就达到了代码复用的目的

但是这样问题就来了,对老师类来说,需要有教学的行为,假如这个方法以抽象方法的形式放在基类,那么对于继承了Person的学生类来说就不对了,因为没有要求学生一定会教学,但是现在学生就得实现这个方法

如果我们把老师的教学的行为作为 老师类的私有, 这时候,小明教小李学习, 就意味着对小明来说,他需要教学的行为, 前前后后看起来就开始矛盾了, 到底怎么处理呢?

策略者模式,就解决了这个问题, 它把行为抽象成了接口,以接口+实现的方式,解决上面的问题, 就上面的例子来说,可以把教学设计成接口,任何类,只要实现了这个接口,就可以教学,而不一定强制要求只有老师才可以实现它

总的来说,策略模式,就是将行为抽象成接口+实现的模式

netty的bossgroup中接收到了新的连接之后会使用选择器Chooser,从WorkerGroup中选择出一个EventLoop, 然后把这个连接注册进选出的 EventLoop

netty的选择器,使用的就是策略者模式,将选择的行为 设计成接口,不同的选择器根据自己不同的需求用不用的方式实现选择器接口

行为接口

  1. @UnstableApi
  2. public interface EventExecutorChooserFactory {
  3. EventExecutorChooser newChooser(EventExecutor[] executors);
  4. @UnstableApi
  5. interface EventExecutorChooser {
  6. EventExecutor next();
  7. }
  8. }

选择器不同的实现:

  1. if (isPowerOfTwo(executors.length)) {// todo 如果是2的指数倍, 返回PowerOfTwoEventExecutorChooser
  2. return new PowerOfTwoEventExecutorChooser(executors);
  3. } else {// todo 否则返回同样的实例
  4. return new GenericEventExecutorChooser(executors);
  5. }

根据线程执行器的数量确定使用那种具体的行为

  1. private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
  2. private final AtomicInteger idx = new AtomicInteger();
  3. private final EventExecutor[] executors;
  4. PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
  5. this.executors = executors;
  6. }
  7. @Override
  8. public EventExecutor next() {
  9. return executors[idx.getAndIncrement() & executors.length - 1];
  10. }

主要看它的executors[idx.getAndIncrement() & executors.length - 1]

进行速度更快的与运算

  1. 1 & 1 = 1
  2. 1 & 0 = 0
  3. 0 & 1 = 0

当数组的长度是2的幂次方时, 用二进制表示就是1111… 全是1, 再减去1 ,就是0111…

无论前面的数是谁,对一个 0111… 进行与运算,得到的结果就是从0-0111…大小的数, 循环往复

  1. private final AtomicInteger idx = new AtomicInteger();
  2. private final EventExecutor[] executors;
  3. GenericEventExecutorChooser(EventExecutor[] executors) {
  4. this.executors = executors;
  5. }
  6. @Override
  7. public EventExecutor next() {
  8. // todo 从0开始到最后一个, 再从零开始,到最后一个
  9. return executors[Math.abs(idx.getAndIncrement() % executors.length)];
  10. }

主要的一步就是Math.abs(idx.getAndIncrement() % executors.length)
可以看到,从0开始一直往后对数组的长度取余数,小数对大数取余数=小数, 保证了数组的下标从0开始递增, 自己对自己取余数=0,保证了最大值是 数组的长度减一, 如此往复

版权声明:本文为ZhuChangwu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ZhuChangwu/p/11237899.html