目录
- 策略模式
- 策略模式结构
- 策略模式应用场景
- 策略模式优缺点
- 练手题目
- 题目描述
- 输入描述
- 输出描述
- 题解
策略模式
策略模式,又称政策模式,是一种行为型设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。
策略模式结构
- 上下文(Context)维护指向具体策略的引用,且仅通过策略接口与该对象进行交流。
- 策略 (Strategy) 接口是所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。
- 具体策略 (Concrete Strategies) 实现了上下文所用算法的各种不同变体。
- 当上下文需要运行算法时, 它会在其已连接的策略对象上调用执行方法。 上下文不清楚其所涉及的策略类型与算法的执行方式。
- 客户端 (Client) 会创建一个特定策略对象并将其传递给上下文。 上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
通用代码结构示例
//策略接口声明了某个算法各个不同版本间所共有的操作。
interface Strategy{
...
}
//具体策略会在遵循策略基础接口的情况下实现算法。
class ConcreteStrategies implements Strategy{
...
}
//抽象生成器类
class Context{
//抽象策略
private Strategy strategy = null;
//抽象策略设置具体策略
public Context(Strategy s){
this.strategy=s;
}
//封装具体的方法
...
}
//客户端
public class Client{
Stragety stragety = new ConcrateStrategies();
Context context = new Context(Strategy);
...
}
策略模式应用场景
-
当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。
-
当你有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式。
策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。
-
如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
策略模式让你能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来。 不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。
-
当类中使用了复杂条件运算符以在同一算法的不同变体中切换时,可使用该模式。
策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。 原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。
识别方法:策略模式可以通过允许嵌套对象完成实际工作的方法以及允许将该对象替换为不同对象的设置器来识别。
策略模式优缺点
优点:
- 你可以在运行时切换对象内的算法。
- 你可以将算法的实现和使用算法的代码隔离开来。
- 你可以使用组合来代替继承。
- 开闭原则。 你无需对上下文进行修改就能够引入新的策略。
缺点:
- 如果你的算法极少发生改变,那么没有任何理由引入新的类和接口。使用该模式只会让程序过于复杂。
- 客户端必须知晓策略间的不同——它需要选择合适的策略。
- 许多现代编程语言支持函数类型功能, 允许你在一组匿名函数中实现不同版本的算法。 这样, 你使用这些函数的方式就和使用策略对象时完全相同, 无需借助额外的类和接口来保持代码简洁。
练手题目
题目描述
小明家的超市推出了不同的购物优惠策略,你可以根据自己的需求选择不同的优惠方式。其中,有两种主要的优惠策略:
- 九折优惠策略:原价的90%。
- 满减优惠策略:购物满一定金额时,可以享受相应的减免优惠。
具体的满减规则如下:
满100元减5元
满150元减15元
满200元减25元
满300元减40元
请你设计一个购物优惠系统,用户输入商品的原价和选择的优惠策略编号,系统输出计算后的价格。
输入描述
输入的第一行是一个整数 N(1 ≤ N ≤ 20),表示需要计算优惠的次数。
接下来的 N 行,每行输入两个整数,第一个整数M( 0 < M < 400) 表示商品的价格, 第二个整数表示优惠策略,1表示九折优惠策略,2表示满减优惠策略
输出描述
每行输出一个数字,表示优惠后商品的价格
题解
1、初次解决思路,简单的策略模式实现。
import java.util.Scanner;
interface Strategy {
void preferentialMethod(int price);
}
class ConcreteStrategy1 implements Strategy {
public void preferentialMethod(int price) {
double discountedPrice = 0.9 * price;
System.out.println((int) discountedPrice);
}
}
class ConcreteStrategy2 implements Strategy {
public void preferentialMethod(int price) {
if (price >= 300) {
price = price - 40;
} else if (price < 300 && price >=200) {
price = price - 25;
} else if (price < 200 && price >= 150) {
price = price - 15;
} else if (price <150 && price >= 100) {
price = price - 5;
}
System.out.println(price);
}
}
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy(int price) {
strategy.preferentialMethod(price);
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
scanner.nextLine();
for (int i = 0; i < num; i++) {
String input = scanner.nextLine();
String[] parts = input.split(" ");
if (parts.length != 2) {
System.out.println("输入错误!");
break;
}
int price = Integer.parseInt(parts[0]);
int type = Integer.parseInt(parts[1]);
Context context = null;
switch (type) {
case 1:
context = new Context(new ConcreteStrategy1());
break;
case 2:
context = new Context(new ConcreteStrategy2());
break;
default:
System.out.println("无效选择,请输入1或2");
continue;
}
context.executeStrategy(price);
}
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
} finally {
scanner.close();
}
}
}
2、优化后,使用策略枚举类实现。
import java.util.Scanner;
interface Strategy {
void preferentialMethod(int price);
}
//策略枚举类
enum DiscountStrategy implements Strategy {
STRATEGY1 {
@Override
public void preferentialMethod(int price) {
double discountedPrice = 0.9 * price;
System.out.println((int) discountedPrice);
}
},
STRATEGY2 {
@Override
public void preferentialMethod(int price) {
int[][] discountRules = {
{300, 40},
{200, 25},
{150, 15},
{100, 5}
};
for (int[] rule : discountRules) {
if (price >= rule[0]) {
price -= rule[1];
break;
}
}
System.out.println(price);
}
};
public static DiscountStrategy fromType(int type) {
switch (type) {
case 1:
return STRATEGY1;
case 2:
return STRATEGY2;
default:
throw new IllegalArgumentException("无效选择,请输入1或2");
}
}
}
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy(int price) {
strategy.preferentialMethod(price);
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
scanner.nextLine();
for (int i = 0; i < num; i++) {
try {
String input = scanner.nextLine();
String[] parts = input.split(" ");
if (parts.length != 2) {
System.out.println("输入错误!");
continue;
}
int price = Integer.parseInt(parts[0]);
int type = Integer.parseInt(parts[1]);
DiscountStrategy strategy = DiscountStrategy.fromType(type);
Context context = new Context(strategy);
context.executeStrategy(price);
} catch (NumberFormatException e) {
System.out.println("输入格式错误,请输入有效的价格和类型!");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
} finally {
scanner.close();
}
}
}