策略模式(Strategy Pattern)是一种行为设计模式,它定义一系列算法,将每个算法封装起来,并使它们可以互相替换,使得算法可以独立于使用它的客户端变化。以下是实际开发中的典型使用场景、示例及最佳实践:
意图:分离算法的定义与使用,避免多重条件判断。
核心角色:
策略接口(Strategy):定义算法的公共接口(如 PaymentStrategy)。
具体策略(Concrete Strategy):实现策略接口的具体算法(如 CreditCardPayment、AlipayPayment)。
上下文(Context):持有策略引用,负责调用策略(如 Order 类)。
场景 1:支付方式选择
需求:电商平台支持多种支付方式(信用卡、支付宝、微信支付),需动态切换支付逻辑。
// 1. 定义策略接口
interface PaymentStrategy {
void pay(double amount);
}
// 2. 实现具体策略
class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " via Credit Card");
}
}
class AlipayPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " via Alipay");
}
}
// 3. 上下文类(持有策略)
class Order {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}
// 4. 客户端调用
public class Main {
public static void main(String[] args) {
Order order = new Order();
// 动态切换策略
order.setPaymentStrategy(new CreditCardPayment());
order.checkout(100.0); // 输出: Paid 100.0 via Credit Card
order.setPaymentStrategy(new AlipayPayment());
order.checkout(200.0); // 输出: Paid 200.0 via Alipay
}
}
优势:新增支付方式(如微信支付)只需添加新策略类,无需修改 Order 类。
场景 2:排序算法切换
需求:根据数据规模动态选择排序算法(快速排序、归并排序)。
// 策略接口
interface SortStrategy {
void sort(int[] array);
}
// 具体策略
class QuickSort implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("Sorting using QuickSort");
// 实际实现省略
}
}
class MergeSort implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("Sorting using MergeSort");
// 实际实现省略
}
}
// 上下文
class Sorter {
private SortStrategy strategy;
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void executeSort(int[] array) {
strategy.sort(array);
}
}
// 使用示例
Sorter sorter = new Sorter();
int[] data = {5, 2, 9, 1};
sorter.setStrategy(new QuickSort());
sorter.executeSort(data); // 输出: Sorting using QuickSort
sorter.setStrategy(new MergeSort());
sorter.executeSort(data); // 输出: Sorting using MergeSort
场景 3:折扣策略(电商促销)
需求:根据活动类型(无折扣、满减、会员折扣)动态计算价格。
interface DiscountStrategy {
double applyDiscount(double originalPrice);
}
class NoDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price;
}
}
class FullReductionDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price >= 100 ? price - 20 : price;
}
}
class ShoppingCart {
private DiscountStrategy discountStrategy;
public void setDiscountStrategy(DiscountStrategy strategy) {
this.discountStrategy = strategy;
}
public double checkout(double price) {
return discountStrategy.applyDiscount(price);
}
}
// 使用示例
ShoppingCart cart = new ShoppingCart();
cart.setDiscountStrategy(new FullReductionDiscount());
double finalPrice = cart.checkout(150.0); // 输出 130.0
(1)使用 Lambda 简化(Java 8+)
若策略接口只有一个方法,可直接用 Lambda 或方法引用替代具体策略类。
// 支付策略示例的简化版
Order order = new Order();
order.setPaymentStrategy(amount -> System.out.println("Paid " + amount + " via WeChat Pay"));
order.checkout(50.0);
(2)策略枚举
适用于策略类型固定的场景。
enum DiscountType implements DiscountStrategy {
NO_DISCOUNT {
public double applyDiscount(double price) { return price; }
},
MEMBER_DISCOUNT {
public double applyDiscount(double price) { return price * 0.9; }
};
}
// 使用
double price = DiscountType.MEMBER_DISCOUNT.applyDiscount(200.0);
(3)策略工厂
集中管理策略的创建逻辑。
class PaymentStrategyFactory {
static PaymentStrategy getStrategy(String type) {
switch (type) {
case "alipay": return new AlipayPayment();
case "credit": return new CreditCardPayment();
default: throw new IllegalArgumentException("Unsupported payment type");
}
}
}
// 客户端调用
PaymentStrategy strategy = PaymentStrategyFactory.getStrategy("alipay");
代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。
代理模式包含三个主要角色:
抽象主题(Subject): 定义真实主题和代理主题的共同接口
真实主题(Real Subject): 实现真正的业务逻辑
代理(Proxy): 控制对真实主题的访问,可以在调用真实主题前后添加额外操作
静态代理是在编译时就确定代理关系的实现方式。
示例代码
// 1. 定义抽象主题
interface Image {
void display();
}
// 2. 定义真实主题
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
}
// 3. 定义代理
class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
// 4. 使用代理
public class StaticProxyDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 第一次调用会创建真实对象
image.display();
// 第二次调用直接使用已创建的真实对象
image.display();
}
}
静态代理特点
优点:简单直观
缺点:每个真实主题都需要一个代理类,代码量大
动态代理是在程序运行时,通过反射机制动态生成的代理类。Java提供了java.lang.reflect.Proxy类来实现动态代理。Java提供了两种主要方式:
JDK动态代理(基于接口)
CGLIB动态代理(基于类)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 定义抽象主题
interface UserService {
void addUser(String name);
void deleteUser(String name);
}
// 2. 定义真实主题
class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户: " + name);
}
@Override
public void deleteUser(String name) {
System.out.println("删除用户: " + name);
}
}
// 3. 定义调用处理器
class UserServiceInvocationHandler implements InvocationHandler {
private Object target;
public UserServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--- 前置处理 ---");
Object result = method.invoke(target, args);
System.out.println("--- 后置处理 ---");
return result;
}
}
// 4. 使用动态代理
public class JdkProxyDemo {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new UserServiceInvocationHandler(realService)
);
proxy.addUser("张三");
proxy.deleteUser("李四");
}
}
需要添加CGLIB依赖:
示例代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
// 1. 定义真实主题(不需要接口)
class ProductService {
public void addProduct(String name) {
System.out.println("添加产品: " + name);
}
public void deleteProduct(String name) {
System.out.println("删除产品: " + name);
}
}
// 2. 定义方法拦截器
class ProductMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("--- 前置处理 ---");
Object result = proxy.invokeSuper(obj, args);
System.out.println("--- 后置处理 ---");
return result;
}
}
// 3. 使用CGLIB代理
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
enhancer.setCallback(new ProductMethodInterceptor());
ProductService proxy = (ProductService) enhancer.create();
proxy.addProduct("手机");
proxy.deleteProduct("电脑");
}
}
远程代理:为一个对象在不同地址空间提供局部代表,以便隐藏实际对象存在于不同地址空间的事实。
虚拟代理:根据需要创建开销大的对象,通过代理对象来控制对真实对象的访问。
安全代理:控制真实对象的访问权限,确保用户只能进行允许的操作。
智能引用代理:在访问对象时执行一些附加操作,如记录访问日志、统计访问次数等。
AOP实现:Spring AOP就是基于动态代理实现的
优点:
职责清晰,真实主题只需关注核心业务
高扩展性,可以在不修改真实主题的情况下增加功能
作为调用方和真实主题之间的中介,保护真实主题
缺点:
增加了系统复杂度
在客户端和真实主题之间增加了代理对象,可能会影响性能
特性 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
基于 | 接口 | 类 |
性能 | 较慢(反射调用) | 较快(方法索引直接调用) |
生成方式 | 反射 | 字节码生成 |
方法final限制 | 无 | 无法代理final方法 |
Spring AOP 主要使用两种代理模式:
JDK 动态代理(基于接口)
CGLIB 代理(基于类)