策略者模式的特點
在設計類的繼承體系時,我們會刻意的把公共的部分都提取到基類中
比如先設計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,保證了最大值是 陣列的長度減一, 如此往復