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
函数即可。
在上面的代码中我们可以看到,虽然外部调用的是DiscountManager
的getDiscountByCustomer
接口,但是其实这个函数并没有自己计算具体的折扣,而是委托其内部聚合的Startegy
对象来计算折扣。正式因为我们在DiscountManager
中使用的是委托的方式,委托是一种弱关联的方式,这使得我们可以很方便的整体替换折扣算法。
对比之前的Bridge
模式,我们会发现该两者的实现方式很相似,都是通过委托的方式来实现功能,两种模式的具体UML
类图如下:
策略模式:
Bridge模式:
可以看到,两个UML类图的结构几乎一样,不同的地方就是,在Bridge模式中,在类的功能层次层次可能会增加新的子类。这与两个模式的用途有关:
策略模式的目的在于能够方便的、动态替换策略算法。而Bridge
模式主要的特点是,将类的功能层次和实现层次分离开,使得二者可以独立的扩展。言下之意,在策略模式中,我们调用策略的Context
一般不会频繁的更改,我们聚焦的是Strategy
类的替换,而在Bridge模式是为了Abstraction
和Implementor
都可以很方便地扩展。