目录
- 一、场景
- 二、不使用策略模式
- 三、使用策略模式
-
- 1、不优雅的实现
- [2、策略模式 + 简单工厂模式](#2、策略模式 + 简单工厂模式)
-
- [2.1 代码](#2.1 代码)
- [2.2 优点](#2.2 优点)
- [2.3 另一种实现方式](#2.3 另一种实现方式)
- 四、个人思考
一、场景
- 程序员除了会
ctrl+ c
+ctrl + v
之外,最擅长的莫过于写if...else...
了,哈哈:) - 有些场景下,我们可以预料到,会写非常多的
if...else if...else if ... else if ...
。这时候,可以使用策略模式。
1、题目描述 【来源】
小明家的超市推出了不同的购物优惠策略,你可以根据自己的需求选择不同的优惠方式。其中,有两种主要的优惠策略:
- 九折优惠策略:原价的90%。
- 满减优惠策略:购物满一定金额时,可以享受相应的减免优惠。
具体的满减规则如下:满100元减5元
满150元减15元
满200元减25元
满300元减40元
请你设计一个购物优惠系统,用户输入商品的原价和选择的优惠策略编号,系统输出计算后的价格。
2、输入描述
输入的第一行是一个整数 N(1 ≤ N ≤ 20),表示需要计算优惠的次数。
接下来的 N 行,每行输入两个整数,第一个整数M( 0 < M < 400) 表示商品的价格, 第二个整数表示优惠策略,1表示九折优惠策略,2表示满减优惠策略
3、输出描述
每行输出一个数字,表示优惠后商品的价格
4、输入示例
4
100 1
200 2
300 1
300 2
5、输出示例
90
175
270
260
二、不使用策略模式
- 代码:
java
public class Application {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
for (int i = 0; i < n; i++) {
int price = scanner.nextInt();
int strategyNum = scanner.nextInt();
if (strategyNum == 1) {
System.out.println((int) (price * 0.9));
} else if (strategyNum == 2) {
if (price < 100) {
System.out.println(price);
} else if (price < 150) {
// 满100元减5元
System.out.println(price - 5);
} else if (price < 200) {
// 满150元减15元
System.out.println(price - 15);
} else if (price < 300) {
// 满200元减25元
System.out.println(price - 25);
} else {
// 满300元减40元
System.out.println(price - 40);
}
} else {
throw new RuntimeException("Invalid strategy number");
}
}
}
}
- 问题:
- 可以预见的是,随着发展,优惠策略肯定不止一种。
- 因此,可以用策略模式优化代码。
三、使用策略模式
1、不优雅的实现
- 策略
java
public interface Strategy<T> {
void execute(T data);
}
@AllArgsConstructor
public class DiscountStrategy implements Strategy<Integer> {
private Double discount;
@Override
public void execute(Integer data) {
System.out.println((int) (data * discount));
}
}
public class FullMinusStrategy implements Strategy<Integer> {
@Override
public void execute(Integer price) {
if (price < 100) {
System.out.println(price);
} else if (price < 150) {
// 满100元减5元
System.out.println(price - 5);
} else if (price < 200) {
// 满150元减15元
System.out.println(price - 15);
} else if (price < 300) {
// 满200元减25元
System.out.println(price - 25);
} else {
// 满300元减40元
System.out.println(price - 40);
}
}
}
- 客户端
java
public class Application {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
Strategy strategy = null;
for (int i = 0; i < n; i++) {
int price = scanner.nextInt();
int strategyNum = scanner.nextInt();
switch (strategyNum) {
case 1:
strategy = new DiscountStrategy(0.9D);
break;
case 2:
strategy = new FullMinusStrategy();
break;
default:
new RuntimeException("Strategy not found");
}
strategy.execute(price);
}
}
}
- 啊这,这不还是写
if...esle
吗?(switch只是对if...else
的美化而已) - 我们其实已经知道<1, DiscountStrategy>, <2, FullMinusStrategy>,因此,可以提前建立联系。
2、策略模式 + 简单工厂模式
2.1 代码
- 策略和上面一样
java
public interface Strategy<T> {
...
}
@AllArgsConstructor
public class DiscountStrategy implements Strategy<Integer> {
...
}
public class FullMinusStrategy implements Strategy<Integer> {
...
}
- 对策略的封装:
java
public class StrategyManager {
private static final Map<Integer, Strategy> strategies = new HashMap<>();
public StrategyManager() {
strategies.put(1, new DiscountStrategy(0.9D));
strategies.put(2, new FullMinusStrategy());
}
public void execute(Integer strategyNum, Integer data) {
Strategy strategy = strategies.get(strategyNum);
if (strategy == null) {
throw new RuntimeException("Strategy not found");
}
strategy.execute(data);
}
}
- 客户端:
java
public class Application {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
StrategyManager strategyManager = new StrategyManager();
for (int i = 0; i < n; i++) {
int price = scanner.nextInt();
int strategyNum = scanner.nextInt();
strategyManager.execute(strategyNum, price);
}
}
}
2.2 优点
- 即使再增加一种策略,也不用修改客户端的代码。
- 然而,没有Spring的辅助下,需要自己构建
Map<Integer, Strategy> strategies
,这使得增加一种策略时,还得修改StrategyManager类。
2.3 另一种实现方式
- 策略:
java
public interface Strategy<T, R> {
boolean match(R matchContext);
void execute(T data);
}
@AllArgsConstructor
public class DiscountStrategy implements Strategy<Integer, Integer> {
private Double discount;
@Override
public boolean match(Integer matchContext) {
return matchContext == 1;
}
@Override
public void execute(Integer data) {
System.out.println((int) (data * discount));
}
}
public class FullMinusStrategy implements Strategy<Integer, Integer> {
@Override
public boolean match(Integer matchContext) {
return matchContext == 2;
}
@Override
public void execute(Integer price) {
if (price < 100) {
System.out.println(price);
} else if (price < 150) {
// 满100元减5元
System.out.println(price - 5);
} else if (price < 200) {
// 满150元减15元
System.out.println(price - 15);
} else if (price < 300) {
// 满200元减25元
System.out.println(price - 25);
} else {
// 满300元减40元
System.out.println(price - 40);
}
}
}
- 对策略的封装:
java
public class StrategyManager {
// private static final Map<Integer, Strategy> strategies = new HashMap<>();
private static final List<Strategy> strategies = new ArrayList<>();
public StrategyManager() {
strategies.add(new DiscountStrategy(0.9D));
strategies.add(new FullMinusStrategy());
}
public void execute(Integer strategyNum, Integer data) {
Strategy strategy = strategies.stream()
.filter(s -> s.match(strategyNum))
.findFirst()
.orElse(null);
if (strategy == null) {
throw new RuntimeException("Strategy not found");
}
strategy.execute(data);
}
}
- 客户端和上面一样。
个人觉得这种方式更灵活。
四、个人思考
- 当遇到写非常多的
if...else if...else if ... else if ...
时,说明<规则,处理>是确定的。 - 那么完全可以抽象为:
java
public interface Strategy<T, R> {
boolean match(R matchContext); // 匹配规则
void execute(T data); // 处理数据
}
各种具体的xxxStrategy
java
@Component
public class StrategyManager {
@Autowired
private List<Strategy> strategies;
public void execute(Integer strategyNum, Integer data) {
Strategy strategy = strategies.stream()
.filter(s -> s.match(strategyNum))
.findFirst()
.orElse(null);
if (strategy == null) {
throw new RuntimeException("Strategy not found");
}
strategy.execute(data);
}
}
- 小插曲,后续会写文章解释。
java
@Component
public class StrategyManager {
// 字段注入,不支持静态变量。
@Autowired
private static List<Strategy> strategies;
...
}