这是我参与「第四届青训营 」笔记创作活动的的第11天
为什么要使用策略模式
设想你正在给某家超市编写收银系统,要求该系统要根据当天的优惠策略来决定收费,比如:
- 如果当天全场8折,那么结账时价格就要乘以0.8
- 如果当天满100减20,那么结账时就要判断当前总价是否达到100元
我们可以很简单地写出第一版代码:
class Payment {
public double pay(double price,String strategy) {
switch (strategy) {
case "20%off":
return price * 0.8;
case "100-20":
return price >= 100 ? price - 20 : price;
default:
return price;
}
}
}
public class Main {
public static void main(String[] args) {
Payment payment = new Payment();
double finalPrice;
finalPrice = payment.pay(120, "20%off");
System.out.println("收银:" + finalPrice);
finalPrice = payment.pay(120, "100-20");
System.out.println("收银:" + finalPrice);
}
}
然而如果后面还有新的优惠策略的话,我们就不得不在Payment.pay方法的switch中多加几个case,这样违背了开闭原则,十分不优雅,而且一不小心容易破坏之前已经测试通过的部分。
这时候我们就希望能够将结账这个动作和优惠策略这两个部分解耦,结账的动作永远不会发生变化,当我们需要增改优惠策略时,只需要关注增改的那一部分即可,于是策略模式就呼之欲出。
定义
策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
模式结构
策略模式的主要角色如下。
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
代码实现
public class Main {
public static void main(String[] args) {
Context c = new Context();
double price = 120;
double finalPrice;
Strategy discount = new Discount();
c.setStrategy(discount);
finalPrice = c.strategy(price);
System.out.println("收银:" + finalPrice);
Strategy manJian = new ManJian();
c.setStrategy(manJian);
c.strategy(price);
System.out.println("收银:" + finalPrice);
}
}
//抽象策略类
interface Strategy {
public double strategy(double price); //策略方法
}
//打八折
class Discount implements Strategy {
@Override
public double strategy(double price) {
return price * 0.8;
}
}
//满减
class ManJian implements Strategy {
@Override
public double strategy(double price) {
return price >= 100 ? price - 20 : price;
}
}
//环境类
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public double strategy(double price) {
return strategy.strategy(price);
}
}