学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)
本文先介绍了策略模式的概念及简单实现。再贴了netty中对策略模式的实现。最后总结了一点点思考。
策略模式-概念、实现及netty中的策略模式
1、概念
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以互相替换。策略模式让算法独立于使用它们的客户而独立变化。
适用:实现某一个功能可以有很多种算法或策略,根据实际情况选择不同的算法或者策略来完成该功能。例如有个排序功能,可以选择插入排序、归并排序、冒泡排序等实现。
- 针对同一类型问题的多种处理方式,仅仅是具体的行为有差别。
- 需要安全地封装多种同一类型的操作时。
- 出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体的子类时。
1.1、角色
- Context:用来操作策略的上下文环境
- Stragety:策略的抽象
- ConcreteStrategy:策略的具体实现
2、实现
考虑这么一个场景,计算价格:如果乘坐公交2元,乘坐地铁3元
策略接口类
public interface CalculateStragety {
int calculatePrice();
}
策略实现类
public class SubwayStrategy implements CalculateStragety {
@Override
public int calculatePrice() {
return 3;
}
}
public class BusStragety implements CalculateStragety {
@Override
public int calculatePrice() {
return 2;
}
}
策略上下文
public class CalculateContext {
private CalculateStragety calculateStragety;
public void setCalculateStragety(CalculateStragety calculateStragety) {
this.calculateStragety = calculateStragety;
}
public int calculatePrice() {
return calculateStragety.calculatePrice();
}
}
测试类
public static void main(String[] args) {
CalculateContext calculateContext = new CalculateContext();
calculateContext.setCalculateStragety(new SubwayStrategy());
System.out.println("地铁:" + calculateContext.calculatePrice() + "元");
calculateContext.setCalculateStragety(new BusStragety());
System.out.println("公交:" + calculateContext.calculatePrice() + "元");
}
输出
地铁:3元
公交:2元
乍一看,似乎还没直接if-else来的方便。但是在复杂业务的情况下,策略模式简化了代码结构,增强了系统的可读性和可扩展性(比臃肿的if-else看着舒服,也方便维护)。
上面的测试类中,似乎还留着一个问题:策略的选择还没和调用者完全解耦,调用者还需要自己去new策略,可能有if(情况A){A策略} else {B策略}的情况,我们希望调用者不需要关心策略的选择。这时候,可以使用工厂模式配合策略模式去使用,用工厂类帮我们去根据不同的情况选择不同的策略。下面我们要看到的正是这么个例子。
3、netty中的策略模式
EventExecutorChooser作用是选择一个NioEventLoop。
NioEventLoop:它负责处理网络I/O的读写事件。我们知道netty支持I/O多路复用,当一个连接进来的时候,需要有一个NioEventLoop与它做关联。而DefaultEventExecutorChooserFactory就是根据不同的策略去获取EventExecutorChooser,EventExecutorChooser再为连接选择一个NioEventLoop(NioEventLoop实现了EventExecutor接口)
至于netty中 PowerOfTwoEventExecutorChooser 和 GenericEventExecutorChooser 的next在不同长度下效率不同,博主没看懂,总之就是这么个意思。
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();
private DefaultEventExecutorChooserFactory() { }
@SuppressWarnings("unchecked")
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
//根据数组长度选择EventExecutorChooser,如果长度是2的幂次,使用PowerOfTwoEventExecutorChooser,否则使用GenericEventExecutorChooser
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
private static boolean isPowerOfTwo(int val) {
return (val & -val) == val;
}
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];
}
}
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
}
4、思考
策略模式用来分离算法,在相同的行为抽象下有不同的具体实现策略。
策略模式非常符合单一职责原则(SRP),就一个类而言应该仅有一个引起它变化的原因。客户端的职责是通过上下文发出一个动作,工厂类的职责是帮我们选择一个策略。具体的策略类只关心自己这个动作的实现。这更体现了面向对象的程序设计思想。不过随着策略的增加,子类也会变得特别多(但大多情况下相比臃肿的if-else,应该是好得多)。
[1] Android源码设计模式解析与实战
[2] https://zhuanlan.zhihu.com/p/91092394