设计模式:策略模式-概念、实现及netty中的策略模式

学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)

本文先介绍了策略模式的概念及简单实现。再贴了netty中对策略模式的实现。最后总结了一点点思考。

策略模式-概念、实现及netty中的策略模式

1、概念

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以互相替换。策略模式让算法独立于使用它们的客户而独立变化。

适用:实现某一个功能可以有很多种算法或策略,根据实际情况选择不同的算法或者策略来完成该功能。例如有个排序功能,可以选择插入排序、归并排序、冒泡排序等实现。

  1. 针对同一类型问题的多种处理方式,仅仅是具体的行为有差别。
  2. 需要安全地封装多种同一类型的操作时。
  3. 出现同一抽象类有多个子类,而又需要使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值