在软件开发中,我们经常会遇到需要根据不同条件执行不同逻辑的场景。新手程序员往往会直接使用if-else语句来处理这种情况,但随着业务逻辑的复杂化,这种写法很快就会变得难以维护。让我们看一个典型的例子:
java复制public class Calculator {
public int calculate(int num1, int num2, String operation) {
if ("add".equals(operation)) {
return num1 + num2;
} else if ("subtract".equals(operation)) {
return num1 - num2;
} else if ("multiply".equals(operation)) {
return num1 * num2;
} else {
throw new IllegalArgumentException("Unknown operation");
}
}
}
这段代码虽然简单直接,但存在几个明显的问题:
策略模式通过将算法封装到独立的类中来解决这些问题。它由三个核心组件组成:
让我们用策略模式重构上面的计算器示例:
java复制// 策略接口
public interface CalculationStrategy {
int calculate(int num1, int num2);
}
// 具体策略类
public class AdditionStrategy implements CalculationStrategy {
@Override
public int calculate(int num1, int num2) {
return num1 + num2;
}
}
public class SubtractionStrategy implements CalculationStrategy {
@Override
public int calculate(int num1, int num2) {
return num1 - num2;
}
}
// 上下文类
public class Calculator {
private CalculationStrategy strategy;
public void setStrategy(CalculationStrategy strategy) {
this.strategy = strategy;
}
public int execute(int num1, int num2) {
return strategy.calculate(num1, num2);
}
}
在实际项目中实现策略模式时,有几个关键点需要注意:
下面是一个更完整的示例,展示了如何动态选择策略:
java复制public class StrategyFactory {
private static final Map<String, CalculationStrategy> strategies = new HashMap<>();
static {
strategies.put("add", new AdditionStrategy());
strategies.put("subtract", new SubtractionStrategy());
strategies.put("multiply", new MultiplicationStrategy());
}
public static CalculationStrategy getStrategy(String operation) {
CalculationStrategy strategy = strategies.get(operation);
if (strategy == null) {
throw new IllegalArgumentException("Unknown operation: " + operation);
}
return strategy;
}
}
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// 动态选择策略
calculator.setStrategy(StrategyFactory.getStrategy("add"));
System.out.println("10 + 5 = " + calculator.execute(10, 5));
calculator.setStrategy(StrategyFactory.getStrategy("multiply"));
System.out.println("10 * 5 = " + calculator.execute(10, 5));
}
}
优点:
缺点:
提示:当策略数量较少且固定时,可以使用枚举来实现策略模式,这样可以减少类的数量。
SPI(Service Provider Interface)是Java提供的一种服务发现机制。它允许第三方为接口提供实现,系统可以在运行时动态发现和加载这些实现。
SPI的核心思想是:
让我们通过一个简单示例了解Java SPI的工作机制:
java复制package com.example.spi;
public interface MessageService {
String getMessage();
}
java复制package com.example.spi.impl;
public class EmailService implements MessageService {
@Override
public String getMessage() {
return "Email message";
}
}
code复制com.example.spi.impl.EmailService
java复制ServiceLoader<MessageService> services = ServiceLoader.load(MessageService.class);
for (MessageService service : services) {
System.out.println(service.getMessage());
}
JDBC是Java SPI的经典应用场景。在JDBC 4.0之前,我们需要手动加载驱动:
java复制Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, password);
JDBC 4.0之后使用了SPI机制,驱动会自动加载。这是因为:
优点:
缺点:
Dubbo基于Java SPI机制进行了增强,主要改进包括:
Dubbo SPI的使用步骤:
java复制@SPI("default")
public interface Robot {
void sayHello();
}
java复制public class OptimusPrime implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}
code复制optimusPrime=com.example.OptimusPrime
bumblebee=com.example.Bumblebee
java复制ExtensionLoader<Robot> loader = ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = loader.getExtension("optimusPrime");
optimusPrime.sayHello();
java复制@SPI("netty")
public interface Transporter {
@Adaptive({"server", "transport"})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
}
java复制public class RobotWrapper implements Robot {
private Robot robot;
public RobotWrapper(Robot robot) {
this.robot = robot;
}
@Override
public void sayHello() {
System.out.println("Before say hello");
robot.sayHello();
System.out.println("After say hello");
}
}
java复制@Activate(group = "provider")
public class SimpleRobot implements Robot {
// 实现代码
}
策略模式适合以下场景:
SPI机制适合以下场景:
策略模式实践:
SPI实践:
Dubbo SPI实践:
在实际项目中,我经常将策略模式和SPI机制结合使用。例如,在支付系统中,使用SPI机制加载不同的支付策略,然后根据交易类型选择合适的策略进行处理。这种组合既保持了系统的扩展性,又保证了策略选择的灵活性。