策略模式
● 避免冗长的if,else判断或switch分支判断
● 提供框架的拓展点
原理和实现
定义一族算法类,将每个算法分别封装起来,让它们可以互相替换
策略模式主要包含以下角色:
● 策略接口(Strategy):定义所有支持的算法的公共接口。客户端使用这个接口与具体策略进行交互
● 具体策略(Concrete Strategy):实现策略接口的具体策略类。这些类封装了实际的算法逻辑。
● 上下文(Context):持有一个策略对象,用于与客户端进行交互。
简单实现
让我们以一个简单的例子来说明策略模式:假设我们要实现一个计算器,支持加法、 减法和乘法运算。我们可以使用策略模式将各种运算独立为不同的策略,并让客户端根据需要选择和使用不同的策略。
- 定义一个策略接口
java
package com.hillky.desgin_learn.category;
public interface Operation {
double execute(double num1,double num2);
}
- 创建具体策略类来实现加法、减法和乘法运算
java
package com.hillky.desgin_learn.category;
public class Addition implements Operation{
@Override
public double execute(double num1, double num2) {
return num1+num2;
}
}
package com.hillky.desgin_learn.category;
public class Subtraction implements Operation{
@Override
public double execute(double num1, double num2) {
return num1-num2;
}
}
package com.hillky.desgin_learn.category;
public class Multiplication implements Operation{
@Override
public double execute(double num1, double num2) {
return num1*num2;
}
}
- 我们创建一个上下文类 Calculator ,让客户端可以使用这个类来执行不同的运算
java
package com.hillky.desgin_learn.category;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class Calculator {
private Operation operation;
public Calculator(Operation operation) {
this.operation = operation;
}
public double executeOperation(double num1,double num2){
return operation.execute(num1,num2);
}
}
- 现在,客户端可以使用 Calculator 类来执行不同的运算:
java
package com.hillky.desgin_learn.category;
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.setOperation(new Addition());
System.out.println("10 + 5 = " + calculator.executeOperation(10, 5));
calculator.setOperation(new Subtraction());
System.out.println("10 - 5 = " + calculator.executeOperation(10, 5));
calculator.setOperation(new Multiplication());
System.out.println("10 * 5 = " + calculator.executeOperation(10,5));
}
}
优点:
● 提高代码的可维护性和可扩展性。
● 符合开闭原则(对增加开,对修改关闭)
● 避免使用多重条件判断
缺点
● 客户端需要了解所有的策略
● 增加了类的数量。策略模式会导致程序中具体策略类的数量增加,这可能会导致代码的复杂性增加。
优化if-else分支
普通策略模式
场景:是一段代码屎山,需求是根据不同的报文格式解析报文,下面说一下对这顿屎山的优化
- 初始状态
java
package com.hillky.desgin_learn.category.messageParse;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Message {
private String content;
private String type;
}
package com.hillky.desgin_learn.category.messageParse;
public class MessageParser {
public void parseMessage(Message message) {
String messageType = message.getType();
if ("XML".equalsIgnoreCase(messageType)) {
// 解析 XML 报文
System.out.println("解析 XML 报文: " + message.getContent());
} else if ("JSON".equalsIgnoreCase(messageType)) {
// 解析 JSON 报文
System.out.println("解析 JSON 报文: " + message.getContent());
} else if ("CSV".equalsIgnoreCase(messageType)) {
// 解析 CSV 报文
System.out.println("解析 CSV 报文: " + message.getContent());
} else {
throw new IllegalArgumentException("未知的报文类型: " + messageType);
}
}
}
- 策略模式优化
java
package com.hillky.desgin_learn.category.messageParse;
public interface MessageParserStrategy {
// 解析报文内容的方法,输入一个 Message 对象,无返回值
void parse(Message message);
}
package com.hillky.desgin_learn.category.messageParse;
// XML 报文解析策略
public class XmlMessageParserStrategy implements MessageParserStrategy{
@Override
public void parse(Message message) {
System.out.println("解析 XML 报文: " + message.getContent());
}
}
package com.hillky.desgin_learn.category.messageParse;
// JSON 报文解析策略
public class JsonMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 JSON报文: " + message.getContent());
}
}
package com.hillky.desgin_learn.category.messageParse;
// CSV 报文解析策略
public class CsvMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 CSV报文: " + message.getContent());
}
}
- 使用策略模式进行报文解析,避免了使用分支判断
java
package com.hillky.desgin_learn.category.messageParse;
public class Client {
public static void main(String[] args) {
MessageParserContext parserContext = new MessageParserContext();
// 使用 XML 报文解析策略
parserContext.setStrategy(new XmlMessageParserStrategy());
parserContext.parseMessage(new Message("XML", "<xml>这是一个 XML 报文</xml>"));
// 使用 JSON 报文解析策略
parserContext.setStrategy(new JsonMessageParserStrategy());
parserContext.parseMessage(new Message("JSON", "{\"message\": \"这是一个 JSON 报文\"}"));
// 使用 CSV 报文解析策略
parserContext.setStrategy(new CsvMessageParserStrategy());
parserContext.parseMessage(new Message("CSV", "这是一个,CSV,报文"));
}
}
结合工厂设计模式
我们可以将策略模式与工厂模式结合,以便根据不同的消息类型自动匹配不同的解析策略。下面是如何实现这个优化的:
- 创建一个MessageParserStrategyFactory,用于根据报文类型创建相应的解析策略
java
package com.hillky.desgin_learn.category.messageParse;
import java.util.HashMap;
import java.util.Map;
public class MessageParserStrategyFactory {
private static final Map<String, MessageParserStrategy> strategies = new
HashMap<>();
static {
strategies.put("XML", new XmlMessageParserStrategy());
strategies.put("JSON", new JsonMessageParserStrategy());
strategies.put("CSV", new CsvMessageParserStrategy());
}
public MessageParserStrategy getStrategy(String messageType){
MessageParserStrategy strategy =
strategies.get(messageType.toUpperCase());
if (strategy == null) {
throw new IllegalArgumentException("未知的报文类型: " + messageType);
}
return strategy;
}
}
- 我们修改 MessageParserContext 类,使其根据报文类型自动选择解析策略
java
package com.hillky.desgin_learn.category.messageParse;
import lombok.Data;
@Data
public class MessageParserContext {
private MessageParserStrategy strategy;
// 设置报文解析策略
public void setStrategy(MessageParserStrategy strategy) {
this.strategy = strategy;
}
// 根据策略解析报文
public void parseMessage(Message message) {
// strategy.parse(message);
MessageParserStrategyFactory.getStrategy(message.getType()).parse(message);
}
}
- 我们的代码可以根据不同的消息类型自动匹配不同的解析策略,而无需手动设置策略。
java
package com.hillky.desgin_learn.category.messageParse;
public class Client {
public static void main(String[] args) {
MessageParserContext parserContext = new MessageParserContext();
// 自动使用 XML 报文解析策略
parserContext.parseMessage(new Message("XML", "<xml>这是一个 XML 报 文</xml>"));
// 自动使用 JSON 报文解析策略
parserContext.parseMessage(new Message("JSON", "{\"message\": \"这是一 个 JSON 报文\"}"));
// 自动使用 CSV 报文解析策略
parserContext.parseMessage(new Message("CSV", "这是一个,CSV,报文"));
}
}
通过将策略模式与工厂模式结合,我们优化了报文解析过程。这样的代码更容易扩展 和维护,因为我们可以通过在工厂类中添加新的解析策略来轻松地支持新的报文类型。
源码使用(结合Gpt找例子)
ssm框架
- Spring中的Resource接口:在Spring框架中,org.springframework.core.io.Resource 接口用于抽象不同类型的资源, 例如文件系统资源、类路径资源、URL资源等。Resource接口就像策略模式中的策略接口,而不同类型的资源类(如 ClassPathResource 、 FileSystemResource 等)就像具体策略。客户端可以根据需要选择和使用不同的资源类。
- Spring中的AOP代理:在Spring AOP中,代理类的创建使用了策略模式。 org.springframework.aop.framework.ProxyFactory 中的 AopProxy 接 口定义了创建代理对象的策略接口,而 JdkDynamicAopProxy 和 CglibAopProxy 这两个类分别为基于JDK动态代理和CGLIB动态代理的具体策 略。客户端可以根据需要选择使用哪种代理方式。
jdk源码
下面我们以 java.util.Comparator 接口为例,展示策略模式在JDK中的应用。
我们可以使用策略模式,通过实现 Comparator 接口,为不同的排序需求提供
不同的比较策略。
- 定义 Student 类
java
package com.hillky.desgin_learn.category.jdk;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Student {
private String name;
private int age;
private double score;
}
- 实现 Comparator 接口,定义不同的比较策略:
java
package com.hillky.desgin_learn.category.jdk;
import java.util.Comparator;
public class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()-o2.getAge();
}
}
package com.hillky.desgin_learn.category.jdk;
import java.util.Comparator;
public class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return (int) (o1.getScore()-o2.getScore());
}
}
- 在客户端代码中,根据需要选择和使用不同的比较策略
java
package com.hillky.desgin_learn.category.jdk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Client {
public static void main(String[] args) {
// 创建一个Student对象的列表
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20, 90.0));
students.add(new Student("Bob", 18, 85.0));
students.add(new Student("Charlie", 22, 88.0));
// 使用年龄比较策略进行排序
Collections.sort(students, new AgeComparator());
System.out.println("按年龄排序: " + students);
// 使用成绩比较策略进行排序
Collections.sort(students, new ScoreComparator());
System.out.println("按成绩排序: " + students);
}
}
使用场景
- 支付系统:在电商或其他在线支付场景中,我们可能需要支持多种支付方式(如信用卡、PayPal、微信支付、支付宝等)。我们可以使用策略模式定义一个支付 接口,并为每种支付方式提供一个具体的实现。客户端可以根据用户的选择使用不同的支付策略。
- 促销策略:在商城系统中,我们可能需要根据不同的促销活动(如满减、打折、 买一送一等)提供不同的折扣策略。我们可以使用策略模式定义一个折扣接口, 并为每种促销活动提供一个具体的实现。客户端可以根据不同的促销活动选择合适的折扣策略。
- 密码加密:在安全领域,我们可能需要对用户密码进行加密,以保护用户数据的 安全。我们可以使用策略模式定义一个加密接口,并为不同的加密算法(如 MD5、SHA-1、SHA-256等)提供具体的实现。客户端可以根据需要选择和使用不同的加密策略。