[设计模式] - No.12 Strategy模式

Strategy模式

Strategy模式又称策略模式,在策略模式中,我们将特定的算法封装起来,从而实现整体地替换算法的功能,以便我们能够按照不同的算法去解决同一个问题。《图解设计模式》这本书中的,策略模式这一章提供的代码示例有些冗长,所以我参考了这篇文章,编写了一个简单的例子,用较少的代码解释什么是策略模式。

假设我们有一个场景,在商场中针对不同的客户的身份,提供不同的优惠方案,最简单的一种实现如下所示:

public class MarketPrice {
    private final String OLD_CUSTOME = "老客户";
    private final String NEW_CUSTOME = "新客户";
    private final String VIP_CUSTOME = "VIP客户";
    public double getDiscount(String custome){
        double discount = 1.0;
        if(OLD_CUSTOME.equals(custome)){
            System.out.println("老客户有9折优惠!");
            discount = 0.9;
        }
        else if(NEW_CUSTOME.equals(custome)){
            System.out.println("新客户没有优惠!");
        }
        else if (VIP_CUSTOME.equals(custome)){
            System.out.println("VIP客户有8优惠!");
            discount = 0.8;
        }
        return discount;
    }
}

这段代码看起来没有什么问题,并且在现有的工程中,很多时候我们也是这样做的。但是,一旦我们希望对现有代码进行扩展,例如,增加一个针对SVIP的优惠信息,我们就必须对MarketPrice进行扩展,在if-else中增加新的语句,这又违反了开闭原则。

在设计模式的学习中,我们发现SOLID原则中,最重要的一个就是开闭原则,其他的原则基本上是为了开闭原则服务的。由于我们希望遵循开闭原则,对修改关闭,对扩展开放,所以我们才希望将抽象和实现分离开,才需要使用里氏替换原则,才需要依赖反转,最终达到解耦的目的。

重新回到代码,我们将MarketPrice中的算法抽离出来,封装成独立的算法,当我们需要不同的优惠策略的时候,只需要替换算法即可。

public interface Strategy {
    public double getDiscount();
}
public class OldCustomerStrategy implements Strategy{
    @Override
    public double getDiscount() {
        System.out.println("老客户有9折优惠!");
        return 0.9;
    }
}
public class VIPCustomerStrategy implements Strategy {
    @Override
    public double getDiscount() {
        System.out.println("VIP客户有8优惠!");
        return 0.8;
    }
}
public class DiscountManager {
    private Strategy strategy;

    public DiscountManager(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public double getDiscountByCustomer(){
        return strategy.getDiscount();
    }

}

在主程序中,我们这样调用:

public class Main {
    public static void main(String args []){
        double product = 2000;

        DiscountManager manager = new DiscountManager(new VIPCustomerStrategy());

        double discount4 = manager.getDiscountByCustomer();
        System.out.println(String.format("VIP客户实际购买价 $%.2f", product*discount4));

        manager.setStrategy(new OldCustomerStrategy());

        double discount5 = manager.getDiscountByCustomer();
        System.out.println(String.format("老客户实际购买价 $%.2f", product*discount5));

    }
}

如果我们希望增加一个针对SVIP的优惠策略,只需要在编写一个新的类,继承Strategy并复写其getDiscount函数即可。

在上面的代码中我们可以看到,虽然外部调用的是DiscountManagergetDiscountByCustomer接口,但是其实这个函数并没有自己计算具体的折扣,而是委托其内部聚合的Startegy对象来计算折扣。正式因为我们在DiscountManager中使用的是委托的方式,委托是一种弱关联的方式,这使得我们可以很方便的整体替换折扣算法。

对比之前的Bridge模式,我们会发现该两者的实现方式很相似,都是通过委托的方式来实现功能,两种模式的具体UML类图如下:

策略模式:

在这里插入图片描述

Bridge模式:
在这里插入图片描述

可以看到,两个UML类图的结构几乎一样,不同的地方就是,在Bridge模式中,在类的功能层次层次可能会增加新的子类。这与两个模式的用途有关:

策略模式的目的在于能够方便的、动态替换策略算法。而Bridge模式主要的特点是,将类的功能层次和实现层次分离开,使得二者可以独立的扩展。言下之意,在策略模式中,我们调用策略的Context一般不会频繁的更改,我们聚焦的是Strategy类的替换,而在Bridge模式是为了AbstractionImplementor都可以很方便地扩展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章