• 作者:老汪软件技巧
  • 发表时间:2024-09-02 10:03
  • 浏览量:

策略模式在实际的开发中很常用,最常见的应用场景是利用它来替换过多的 if-else 嵌套的逻辑判断。除此之外,它还能结合工厂模式给客户端提供非常灵活的使用体验。下面,我们一起来看看吧!

一、模式原理分析

策略模式的原始定义是:定义一系列算法,封装每个算法,并使它们可以互换。策略让算法独立于使用它的客户端而变化。

在这个定义中,策略模式明确表示应当由客户端自己决定在什么样的情况下使用哪些具体的策略。也就是说,服务端是作为一个策略的整体调控者,具体选择运行哪些策略其实是要交给客户端来决定的。比如,压缩文件的时候,你应该会提供一系列的不同压缩策略,比如,gzip、zip 等,至于客户端在什么时候使用 gzip,由客户端自行去决定。同时,gzip 还可以被替换为其他的压缩策略。

策略模式包含三个关键角色:

下面我们再来看看策略模式对应的代码实现

public class Context {
    public void request(Strategy s) {
        s.operation();
    }
}
public interface Strategy {
    void operation();
}
public class StrategyA implements Strategy {
    @Override
    public void operation() {
        System.out.println("=== 执行策略 A ......");
    }
}
public class StrategyB implements Strategy {
    @Override
    public void operation() {
        System.out.println("=== 执行策略 B ......");
    }
}

从上面的代码实现我们能看出,策略模式的本质就是通过上下文信息类来作为中心控制单元,对不同的策略进行调度分配。

二、使用场景分析

策略模式常见的使用场景有以下几种。

为了更好地理解策略模式的使用场景,下面我们还是通过一个简单的例子来演示说明。在日常的网上购物中,我们都希望购买到价格实惠的商品,而网站通常也会定期搞一些促销活动,比如,满 XXX 元减 XX 元、N 折扣、M 元秒杀等。这些活动通常是运营部门来制定的,运营希望能通过商品编号来推荐不同的促销活动。这时,作为系统的开发者,我们就需要开发针对一系列的营销策略进行推荐的系统功能。

那具体该怎么来实现呢?

首先,我们来定义策略 API——PromotionStrategy,每一种促销策略的算法都要实现该接口。该接口有一个 recommand 方法,接收并返回一个 int 对象,返回的就是推荐后可以参加的促销活动。实际上,推荐返回的可能是一个活动对象,这里用简单的数字代替。

public interface PromotionStrategy {
    /**
     * 返回1 代表 可以参加 满减活动
     * 返回2 代表 可以参加 N折优惠活动
     * 返回3 代表 可以参加 M元秒杀活动
     */
    int recommand(String skuId);
}

接下来,我们有三个类实现了 PromotionStrategy 接口,分别代表满减策略、N 折扣优惠活动策略和 M 元秒杀活动策略,类分别是 FullReduceStrategy、NPriceDiscountStrategy 和 MSpikeStrategy。

public class FullReduceStrategy implements PromotionStrategy {
    @Override
    public int recommand(String skuId) {
        System.out.println("=== 执行 满减活动");
        //推荐算法和逻辑写这里
        return 1;
    }
}
public class NPriceDiscountStrategy implements PromotionStrategy {
    @Override
    public int recommand(String skuId) {
        System.out.println("=== 执行 N 折扣优惠活动");
        //推荐算法和逻辑写这里
        return 2;
    }
}
public class MSpikeStrategy implements PromotionStrategy {
    @Override
    public int recommand(String skuId) {
        System.out.println("=== 执行 M 元秒杀活动");
        //推荐算法和逻辑写这里
        return 3;
    }
}

现在我们可以动手实现促销推荐的上下文信息类 Promotional,这里是存储和使用策略的地方。类中有一个 recommand 方法,用于执行推荐策略。它的构造函数有一个 PromotionStrategy 参数,可以在运行期间使用该参数决定使用哪种促销策略。

public class Promotional {
    private final PromotionStrategy strategy;
    public Promotional(PromotionStrategy strategy) {
        this.strategy = strategy;
    }
    public void recommand(String skuId) {
        strategy.recommand(skuId);
    }
}

最后,我们通过一个简单的单元测试代码来看下具体的运行结果。

public class Client {
    public static void main(String[] args) {
        Promotional fullReducePromotional = new Promotional(new FullReduceStrategy());
        fullReducePromotional.recommand("1122334455");
        Promotional nPriceDiscountPromotional = new Promotional(new NPriceDiscountStrategy());
        nPriceDiscountPromotional.recommand("6677889900");
        Promotional mSpikePromotional = new Promotional(new MSpikeStrategy());
        mSpikePromotional.recommand("11335577");
    }
}
//输出结果
=== 执行 满减活动
=== 执行 n折扣优惠活动
=== 执行 m元秒杀活动

三、为什么使用策略模式?

分析完策略模式的原理和使用场景后,我们再来说说使用策略模式的原因,主要有以下三个。

第一个,为了提升代码的可维护性。 在实际开发中,有许多算法可以实现某一功能,如查找、排序等,通过 if-else 等条件判断语句来进行选择非常方便。但是这就会带来一个问题:当在这个算法类中封装了大量查找算法时,该类的代码就会变得非常复杂,维护也会突然就变得非常困难。虽然策略模式看上去比较笨重,但实际上在每一次新增策略时都通过新增类来进行隔离,短期虽然不如直接写 if-else 来得效率高,但长期来看,维护单一的简单类耗费的时间其实远远低于维护一个超大的复杂类。

第二个,为了动态快速地替换更多的算法。 从上面的分析你会发现,策略模式最大的作用在于分离使用算法的逻辑和算法自身实现的逻辑,这样就意味着当我们想要优化算法自身的实现逻辑时就变得非常便捷,一方面可以采用最新的算法实现逻辑,另一方面可以直接弃用旧算法而采用新算法。使用策略模式能够很方便地进行替换。

第三个,为了应对需要频繁更换策略的场景。 比如,用户推荐类场景。特别是对于一些 C 端产品来说,在获取了用户的反馈数据后,会根据用户的特性制定不同的运营策略,这时如果采用 if-else 的方式编码,那么每一次的策略变化都会导致系统代码的修改,从运营的角度看是不可接受的,而采用策略模式就能很容易地解决这个问题。

四、策略模式的优缺点是什么?

通过上面的分析,我们可以得出使用策略模式主要有以下几个优点。

同样,策略模式也不是万能的,它也有一些缺点。

策略模式最大的用处是能在运行时改变代码的算法行为,同时给使用者提供一种可以根据情况来选择算法的途径。

虽然策略模式是一个比较容易理解和使用的设计模式,但是却增加了使用者的难度,因为可能需要在了解了所有的策略后才能做出决策。即便是类似排序这样简单的算法,不同使用者的选择也可能完全不同,如果交给使用者来选择,就意味着使用者需要了解不同排序算法的优劣,才能更好地做出选择。

不过,策略模式对算法起到了很好的封装作用,通过使用算法和创建算法的分离,将算法实现的复杂性放到了子类去解决。同时,策略模式还可以随时进行替换,对于一些老旧的算法,可以很方便地进行替换和升级。