设计模式之策略模式(行为型)

策略模式


策略的特点就是同一种行为不同实现方式同一种行为用代码表述就是接口定义的一个方法。不同的实现方式就是这个接口的多个实现类,分别实现了接口中的方法。

大家可以想,只要是实现了这个接口的类,是不是都得必须实现这个方法,这样就能表达了同一种行为的不同实现方式。

举个例子:ThreadPoolExecutor,这个应该不是很陌生,线程池,下面是线程池的构造方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              // 使用了策略模式
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }


这里只看ThreadPoolExecutor构造方法的最后一个参数RejectedExecutionHandler即可,这个参数的意思是,线程池执行的线程数超出规定的数量以后,再继续执行更多线程的时候,该怎么处理这些超出来的程。

用个比喻来说就是,用管子往一个池子里注水,池子满了以后,想要继续注水的话,应该怎么办,是继续注水让池子里的水溢出来呢?还是说关了阀门,不再注水?还是说把注水的管子移到一边去,就是把这些水浪费了,不让管子再往池子里注水。这些都是处理多余水的策略。当然,等池子里的水不满的时候,还是要重新注水的。

public interface RejectedExecutionHandler {
    // 这个方法就是上面说的 同一种行为
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

这个接口就是上面说的同一种行为。

 


这四个类都是RejectedExecutionHandler的实现,但是他们分别实现了void rejectedExecution(Runnable r, ThreadPoolExecutor executor)这就是说的不同实现方式。

这种通过接口规定方法的写法有什么好处呢?就是方便扩展,还是线程池的例子,现在是只有四种处理超出线程的方式,如果说有以后有更多的处理方式,只需要创建一个类实现RejectedExecutionHandler这个接口就可以了,不需要过多的修改,线程池内部从来不会关心rejectedExecution这个方法的具体实现是什么。

除了方便扩展以外,策略模式还有个好处,就是能消除多个if-else或是switch的判断,这种类型的代码风格。我想大家都见过或是写过一些包含if-else的代码,比如下面这种,这还是看起来算好的

public void getRejectedExecutionHandler(ThreadPoolExecutor executor, Runnable runnable, String type) {
        RejectedExecutionHandler rejectedExecutionHandler = null;
        if (TextUtils.equals(type, "Caller")) {
            rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
        } else if (TextUtils.equals(type, "Abort")) {
            rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
        } else if (TextUtils.equals(type, "DiscardOldest")) {
            rejectedExecutionHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
        } else if (TextUtils.equals(type, "Discard")) {
            rejectedExecutionHandler = new ThreadPoolExecutor.DiscardPolicy();
        }
        rejectedExecutionHandler.rejectedExecution(runnable, executor);
}

有的代码的if-else比这个还要多,我曾经见过十几个if-else的,这种结构的代码一般是根据不同的类型返回不同的对象,然后再执行特定的方法。

其实,这段代码就很好的提现了Java语言的特点,  这是不是满足了多态的特征呢,父类引用指向子类对象。(父类引用就是rejectedExecutionHandler,子类对象就是每一个分支创建的具体类呢)子类是可以互相替换rejectedExecutionHandler的引用,而且不会引起代码错误。

但是如果想要扩展的时候,是不是非常不友好,需要继续添加if-else,代码成了一大坨,越堆越多。而且这也不符合『 对修改关闭,对扩展开放 』的原则。

那么,这段代码怎么通过策略模式来修改一下呢?

首先要找出变化的部分,不用多说,就是if-else这部分代码,增加,删除if-else分支语句,这部分改变的比较频繁,通过观察代码,发现分支语句有个特点就是具有一一对应关系,上面的代码可以看出有四个对应关系,分别是

  • “Caller”               对应    new ThreadPoolExecutor.CallerRunsPolicy();
  • "Abort"                对应    new ThreadPoolExecutor.AbortPolicy();
  • "DiscardOldest"  对应    new ThreadPoolExecutor.DiscardOldestPolicy();
  • "Discard"             对应    new ThreadPoolExecutor.DiscardPolicy(); 

既然存在一一对应关系,那么我们可以联想到某些存储数据的结构,哪一种是存在一一对应关系的,想了想,集合中的Map<K, V>可不就是这种可以实现一一对应关系嘛。

那么,现在要做的事就是把if-else这种格式转换为Map存储,怎么转化?就是提前将这种对应关系保存起来,(可以在静态代码块中,也可以在构造方法中初始化,就是尽可能早的初始化这些,别等到用的时候还没有数据,就糟糕了)

private Map<String, RejectedExecutionHandler> handlerMap = new HashMap<>();

public void putRejectedExecutionHandler() {
     handlerMap.put("Caller", new ThreadPoolExecutor.CallerRunsPolicy());
     handlerMap.put("Abort", new ThreadPoolExecutor.CallerRunsPolicy());
     handlerMap.put("DiscardOldest", new ThreadPoolExecutor.CallerRunsPolicy());
     handlerMap.put("Discard", new ThreadPoolExecutor.CallerRunsPolicy());
}

那么,if-else的方法就可以修改了

public void getRejectedExecutionHandler(ThreadPoolExecutor executor, Runnable runnable, String type) {
      RejectedExecutionHandler rejectedExecutionHandler = handlerMap.get(type);
      rejectedExecutionHandler.rejectedExecution(runnable, executor);
}

这样,getRejectedExecutionHandler方法以后大概率是不用再修改了,如果需要添加新的分支,只需要在Map中继续put添加新的对应关系就可以了。

这里面核心的转变我觉得就是将代码结构转变成数据结构,if-else就是一种代码结构,转变成Map数据存储结构。

策略模式是一种行为型的模式,主要作用还是为了解耦,把复杂方法简单化。是对类中方法(函数)的优化方式。

好了,以上就是我对策略模式的理解。

如果,大家觉得的文章对自己有帮助的话,可以关注一下我的公众号,有啥好的不好的话,都可以在公众号里面留言,交个朋友。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值