策略模式(Strategy Pattern)是相对比较简单的一种设计模式,是通过定义一组算法,并且将每个算法封装到具有共同接口的独立类中,从而使它们之间可以相互转换,使算法在不影响客户端的情况下发生变化。
三个具体实现的策略角色,分别代表无促销、满减促销、打折促销
环境角色Context,屏蔽对具体策略角色的直接访问
以上关于促销案例的策略模式就已经完成,以下是客户端调用的类
策略模式体现了这样两个原则——封装变化和对接口编程而不是对实现编程。设计模式的作者把策略模式定义如下:
Define a family of algorithms, encapsulate each one, and make them interchangeable. [The] Strategy [pattern] lets the algorithm vary independently from clients that use it.(策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而变化。)
策略模式将整个软件构建为可互换部分的松耦合的集合,而不是单一的紧耦合系统。松耦合的软件可扩展性更好,更易于维护且重用性好。
策略模式的UML
策略模式共涉及3种角色:
环境角色(Context):也叫上下文角色,用于屏蔽高层模块对策略、算法的直接访问,它持有一个Strategy类的引用。
抽象策略角色(Strategy):对策略、算法进行抽象,是每个具体策略角色必须实现的。
具体策略角色(Concrete Strategy):用于实现具体的策略、算法。
策略模式的应用
以商场促销为例,有的商品是打折促销,有的商品是满减,还有的是不促销的,可以用策略模式进行优惠算法的封装。
抽象策略角色:
public abstract class StrategyDiscount {
private int num;
private int price;
public StrategyDiscount(int price, int num) {
this.num = num;
this.price = price;
}
public int getNum() {
return num;
}
public int getPrice() {
return price;
}
public abstract double getDiscount();
}
三个具体实现的策略角色,分别代表无促销、满减促销、打折促销
public class NoDiscountStrategy extends StrategyDiscount {
public NoDiscountStrategy(int price,int num) {
super(num, price);
}
@Override
public double getDiscount() {
return getPrice() * getNum();
}
}
public class FixDiscountStrategy extends StrategyDiscount {
public FixDiscountStrategy(int price, int num ) {
super(num, price);
}
@Override
public double getDiscount() {
int sum = getNum() * getPrice();
if(sum > 100 ) { //满100减20
return sum-20;
}else
return sum;
}
}
public class PercentDisCountStrategy extends StrategyDiscount {
public PercentDisCountStrategy(int price, int num) {
super(num, price);
}
@Override
public double getDiscount() {
return 0.8 * getPrice() * getNum(); //打八折
}
}
环境角色Context,屏蔽对具体策略角色的直接访问
public class Context {
private StrategyDiscount strategyDiscount;
public Context(StrategyDiscount strategyDiscount) {
this.strategyDiscount = strategyDiscount;
}
public double contextCalDisc() {
return strategyDiscount.getDiscount();
}
}
以上关于促销案例的策略模式就已经完成,以下是客户端调用的类
public class Client {
public static void main(String[] args) {
Context c1 = new Context(new NoDiscountStrategy(20, 6));
System.out.println("该商品不促销,购买总额: " + c1.contextCalDisc());
Context c2 = new Context(new FixDiscountStrategy(20, 6));
System.out.println("该商品参与满减促销,购买总额 : " + c2.contextCalDisc());
Context c3 = new Context(new PercentDisCountStrategy(20, 6));
System.out.println("该商品参与打八折促销,购买总额 :" + c3.contextCalDisc());
}
}
以上总结策略模式的优点:
1. 策略模式提供了管理相关算法族的办法,可以把公用的特性移动到抽象策略角色中,以提高重用性。
2. 策略模式提供了可以替换继承关系的办法。继承也可以处理多种算法行为,如果不使用策略模式,那么使用算法或行为的环境类就可能有一些子类,每个子类提供一个不同的算法,这样的话,算法或行为的使用者就 和算法本身混在一起,从而不可能在独立的演化。
3. 使用策略可以避免多重条件转移语句。
策略模式的一些缺点:
1. 客户端必须知道所有的具体策略模式,并自行决定调用哪一个,这对拥有大量策略的应用是困难的。
2. 策略模式会造成很多策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样的策略类实例可以被不同的客户端使用。一颗使用享元模式来减少对象的数量。
使用策略模式的场景:
1. 多个类只是在算法或行为上稍有不同的场景。
2. 算法需要自由切换的场景。
3. 需要屏蔽算法规则的场景。