策略模式

策略模式通过定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端

策略模式主要包含以下角色:

  1. 策略接口(Strategy):定义所有支持的算法的公共接口。客户端使用这个接口与具体策略进行交互。
  2. 具体策略(Concrete Strategy):实现策略接口的具体策略类。这些类封装了实际的算法逻辑。
  3. 上下文(Context):持有一个策略对象,用于与客户端进行交互。上下文可以定义一些接口,让客户端不直接与策略接口交互,从而实现策略的封装。

下面我们通过实现一个计算器来简单说明策略模式

首先定义策略接口

1
2
3
public interface Operation {
double execute(double num1, double num2);
}

接下来,我们创建具体策略类来实现加法、减法和乘法运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Addition implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 + num2;
}
public class Subtraction implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 - num2;
}
}
public class Multiplication implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 * num2;
}
}

然后,我们创建一个上下文类 Calculator ,让客户端可以使用这个类来执行不同的运算

1
2
3
4
5
6
7
8
9
public class Calculator {
private Operation operation;
public void setOperation(Operation operation) {
this.operation = operation;
}
public double executeOperation(double num1, double num2) {
return operation.execute(num1, num2);
}
}

然后我们就可以使用策略模式的加减乘了

1
2
3
4
5
6
7
8
9
10
11
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));
}
}

策略模式的优点

  1. 提高代码的可维护性和可扩展性。当需要添加新的算法时,我们只需要实现一个新的具体策略类,而无需修改客户端代码。
  2. 符合开闭原则。策略模式允许我们在不修改现有代码的情况下引入新的策略。
  3. 避免使用多重条件判断。使用策略模式可以消除一些复杂的条件判断语句,使代码更加清晰和易于理解。

策略模式的缺点包括:

  1. 客户端需要了解所有的策略。为了选择合适的策略,客户端需要了解不同策略之间的区别。
  2. 增加了类的数量。策略模式会导致程序中具体策略类的数量增加,这可能会导致代码的复杂性增加。

具体应用

首先策略模式可以结合工厂模式使用优化if分支

下面是一个包含大量if分支的报文解析系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
}
}

接下来,我们可以使用策略模式优化上述代码

首先我们定义一个报文解析策略接口

1
2
3
4
public interface MessageParserStrategy {
// 解析报文内容的方法,输入一个 Message 对象,无返回值
void parse(Message message);
}

然后我们实现各个方法的策略类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// XML 报文解析策略
public class XmlMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 XML 报文: " + message.getContent());
}
}
// JSON 报文解析策略
public class JsonMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 JSON 报文: " + message.getContent());
}
}
// CSV 报文解析策略
public class CsvMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 CSV 报文: " + message.getContent());
}
}

然后结合工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MessageParserStrategyFactory {
private static final Map strategies = new
HashMap<>();
static {
strategies.put("XML", new XmlMessageParserStrategy());
strategies.put("JSON", new JsonMessageParserStrategy());
strategies.put("CSV", new CsvMessageParserStrategy());
}
public static MessageParserStrategy getStrategy(String messageType) {
MessageParserStrategy strategy =
strategies.get(messageType.toUpperCase());
if (strategy == null) {
throw new IllegalArgumentException("未知的报文类型: " + messageType);
}
return strategy;
}
}

然后配置报文解析类

1
2
3
4
5
6
7
public class MessageParserContext {
public void parseMessage(Message message) {
MessageParserStrategy strategy =
MessageParserStrategyFactory.getStrategy(message.getType());
strategy.parse(message);
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
MessageParserContext parserContext = new MessageParserContext();
// 自动使用 XML 报文解析策略
parserContext.parseMessage(new Message("XML", "这是一个 XML 报
文"));
// 自动使用 JSON 报文解析策略
parserContext.parseMessage(new Message("JSON", "{\"message\": \"这是一
个 JSON 报文\"}"));
// 自动使用 CSV 报文解析策略
parserContext.parseMessage(new Message("CSV", "这是一个,CSV,报文"));
}
}

通过策略模式和工厂模式结合,我们优化了报文解析的过程,这样代码更容易扩展和维护,这样代码已经比较完美了,但是如果要添加报文的话,还是要修改工厂类的静态代码块,不符合开闭原则,那么我们还能再怎么优化呢

这里可以通过反射来避免对策略工厂源代码的修改,具体可以通过自定义注解标记策略类,然后工厂类通过搜索含有指定注解的类并且通过反射动态生成这些类,这样代码的可维护性和扩展性就十分好了