likes
comments
collection
share

一起探究何为策略

作者站长头像
站长
· 阅读数 11

前言

这篇文文章主要想探讨一下对于策略的开发习惯,会涉及策略模式。我不敢说我理解的就是正确的,所以是探讨,如果有大佬有独特的见解也可以说出来,让我学习学习。 这类型的文章,很难用语言来组织梳理清楚,说得高大上一点就是只可意会不可言传,说得low一点就是不知从何说起。

1.策略模式

但是说到策略,第一反应能想到的是策略模式,我从网上找个例子大概改一下拿来用。

public interface IPayStrategy {

    void pay(String params);

}
public class PayContext {

    private String name;
    private IPayStrategy iPayStrategy;

    public PayContext(String name, IPayStrategy iPayStrategy) {
        this.name = name;
        this.iPayStrategy = iPayStrategy;
    }

    public void pay(String params) {
        iPayStrategy.pay(params, this);
    }
}
public class AliPayStrategy implements IPayStrategy {

    @Override
    public void pay(String params, PayContext payContext) {
        // todo 支付宝支付
    }

}
public class WxPayStrategy implements IPayStrategy {

    @Override
    public void pay(String params, PayContext payContext) {
        // todo 微信支付
    }

}
PayContext payContext1 = new PayContext("ali", new AliPayStrategy());
payContext1.pay("");
PayContext payContext2 = new PayContext("wx", new WxPayStrategy());
payContext2.pay("");

随便写一下,那个例子大概是这样,然后我再配一些官方的用语,“策略模式定义了一些策略的算法,并将每个算法封装起来,而且它们还可以相互替换,策略模式让算法独立与使用它的客户而独立变化。”

结合起来一看,哦!原来策略模式是这样(我们当时大学的时候第一次看就这感觉,当然现在的大学生比我们那时候的牛逼多了)。一看,这个策略模式,有点感觉,但是又说不出来,那什么是策略模式,心里还是觉得似懂非懂,是觉得有那么一点意思,但是好像又不太清楚,感觉说了跟没说一样。那我再举个例子(换肤,正常模式和夜间模式):

public interface ISkinStrategy {

    void setSkin();

}
public class NormalStrategy implements ISkinStrategy{

    @Override
    public void setSkin(Object obj) {
        obj.getxxx().setRid(R.id.白天皮肤id);
    }
}
public class NightStrategy implements ISkinStrategy{

    @Override
    public void setSkin(Object obj) {
        obj.getxxx().setRid(R.id.夜间皮肤id);
    }
}

这是我乱写的,但是感觉和上面的差不多,那这个算策略模式吗?这时候再回去看,感觉又迷茫了,我不是说别人写的文章有问题,我是觉得当你不了解这个东西的设计思想的时候,光去看Demo,是理解不了的。就算你嘴硬说,我看了之后我理解了,我找到了那种感觉,那你能在开发中合理使用出来吗?不是无脑去仿照这个Demo的形式写代码,是合理的使用。

2. 策略参数

写这篇文章是因为遇到两个场景,也算是机缘巧合吧,但是这两个场景确实很贴切“策略”这个词。

第一件事是几个月前,我去面试,面试官问了我一个问题,搜索框输入内容的补全列表存在背压如何处理,我记得我说了几种办法,用最新或者卡住之类的。 但是现在想想,为什么我要说“或者”,都要不行吗?

第二件事是刚刚看的一篇文章,就因为看了那篇文章才有思路要写下这篇文章。是讲flow的背压处理方式,然后作者就分析源码,我就看到(因为看的比较快)大概是提供了几种方法提供给外部调用。

这时候我就想到了“策略参数”这么一个东西,他的做法不是像我之前一样,只根据需求去提供特定的解决办法,而是把能想到的解决办法都提供出来,至于什么场景要使用什么解决方法,调用者自己去决定。

话说到这里,很多人肯定都会联想到一个东西,没错,线程池。 ThreadPoolExecutor的构造方法中有一个参数,RejectedExecutionHandler

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

了解过线程池的都知道,它表示饱和策略,不了解的可以去了解一下,我就不解释基础的知识了。

线程池,加上flow的做法,都有一个很明显的共同点,没错,他们都有一个表示策略的参数。然后再看ThreadPoolExecutor的设计,就会发现,代码确实不复杂,看起来也就那样,但是这设计确实让人觉得牛逼。

首先他有个默认策略,当你不传任何的策略的时候,就会使用默认策略

private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();
public static class AbortPolicy implements RejectedExecutionHandler {
    /**
     * Creates an {@code AbortPolicy}.
     */
    public AbortPolicy() { }

    /**
     * Always throws RejectedExecutionException.
     *
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     * @throws RejectedExecutionException always
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}

看得出默认的策略就是抛异常,写底层的就是硬气,我也不和你纠结这么多怎么做好,我直接抛异常,你不想要这个结果就自己处理。

其次,他会定义一些基础的策略提供给你使用。CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy之类的,比如DiscardPolicy就是什么都不做

public static class DiscardPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code DiscardPolicy}.
     */
    public DiscardPolicy() { }

    /**
     * Does nothing, which has the effect of discarding task r.
     *
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

最后,你也可以去实现这个接口,然后去写自己的策略。所以这样来看,RejectedExecutionHandler的这个设计就非常的好,扩展性好,也方便在不同的场景下的调用。

到这里其实就能很明显的看出ThreadPoolExecutor用的就是策略模式,而且无论从逻辑设计上,还是代码的这个写法上,都非常的符合策略模式。但是开头我举的那个IPayStrategy,是设策略模式吗?我也不知道怎么回答,这个写法确实符合,但是在实际开发中你会这么写吗?或者说你只看这个Demo,你知道在实际开发中你要怎么写吗?总感觉有点牵强。

3. 思考

我觉得这东西,主要还是要理解它的一个设计思想,是基于什么场景设计出来的,为了解决什么样的问题,理解了它的设计思想之后,你再基于它的设计思想去写代码,不管怎么写,应该都没什么大问题。

所以这东西确实不应该是一开始就随便用一个Demo能解散清楚的,这种东西没有规范,如果硬要一套规范,我确实觉得ThreadPoolExecutor能体现得淋漓尽致。

那么什么是策略呢?我觉得是“为了解决某个问题”那方案,就是策略。ThreadPoolExecutor的RejectedExecutionHandler是为了解决饱和的问题。我上面的面试题,是为了解决背压的问题,所以这个场景,适合用策略。针对背压这个问题,如果你希望在某个情况下用最新的内容,某个情况下抛弃超过缓冲区的内容,那很明显,按照RejectedExecutionHandler来写,最合适。

但是你看最上面举的换肤的例子,就是那个ISkinStrategy,它为了解决什么问题?这么一想,这代码强行这么写就是依托答辩了。