设计模式之策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。在复杂的程序设计中,策略模式可以有效地解决那些条件语句繁杂、具有多重选择的情况,使得代码更加清晰、可维护。
本文将从策略模式的定义出发,介绍其基本结构、工作原理、常见场景以及如何实现。通过一些实例来具体展示策略模式的应用,帮助读者深入理解这个设计模式。
目录
策略模式简介
策略模式属于行为型设计模式,它定义了一系列的算法,并将每个算法封装成一个独立的策略类,然后通过上下文(Context)对象来选择具体的算法执行。这样,不同的算法可以通过不同的策略类进行替换,从而使得系统在运行时更加灵活。
在没有使用策略模式的情况下,常常会通过多重条件判断(如if-else
或者switch-case
)来选择具体的算法。而这些条件判断会随着算法的增加而变得越来越复杂,导致代码的可维护性下降。策略模式通过将算法封装成独立的策略类,避免了冗长的条件判断,提高了代码的可扩展性。
策略模式的结构
策略模式的结构主要由以下几个部分组成:
-
Context(上下文):持有一个策略(Strategy)对象的引用,用于向客户端提供具体的策略接口。Context类可以通过set方法来动态改变当前的策略对象。
-
Strategy(策略接口):策略接口定义了一个算法的公共接口,具体的策略类会实现这个接口来提供具体的算法。
-
ConcreteStrategy(具体策略类):实现了策略接口的具体类,每个具体策略类封装了一个特定的算法实现。
关系图
textCopy Code+------------------+ | Context | |------------------| | - strategy: IStrategy | +------------------+ | | +------------------+ | IStrategy | |------------------| | + execute(): void| +------------------+ | +---------------+----------------+ | | +---------------------+ +---------------------+ | ConcreteStrategyA | | ConcreteStrategyB | |---------------------| |---------------------| | + execute(): void | | + execute(): void | +---------------------+ +---------------------+
策略模式的工作原理
策略模式的工作原理是将算法封装在不同的策略类中,并通过上下文类来切换不同的策略。具体的实现步骤如下:
-
定义策略接口:首先定义一个统一的接口(如
IStrategy
),该接口声明了具体算法的执行方法。 -
实现具体策略类:然后创建多个具体的策略类(如
ConcreteStrategyA
和ConcreteStrategyB
),每个策略类实现了策略接口,并提供了不同的算法实现。 -
创建上下文类:创建一个上下文类(如
Context
),该类内部持有一个策略接口的引用,并通过上下文的set方法动态设置具体的策略。 -
选择策略:客户端可以通过调用上下文的
setStrategy()
方法来改变策略,从而实现不同算法的切换。
通过这种方式,策略模式可以避免大量的if-else
条件判断,提高代码的灵活性和可扩展性。
策略模式的优缺点
优点
-
封装变化:策略模式将算法封装在策略类中,避免了复杂的条件判断,使得算法可以独立变化,客户端无需关心具体的实现。
-
减少条件判断:通过将不同的算法封装在策略类中,避免了
if-else
或switch-case
的使用,减少了复杂的条件语句。 -
增强可扩展性:新的策略类可以在不修改原有代码的基础上增加到系统中,符合开闭原则。
-
符合单一职责原则:每个策略类都只负责一种算法的实现,降低了代码的复杂度。
缺点
-
增加类的数量:策略模式需要为每一种算法创建一个新的策略类,这会导致类的数量增加,可能造成系统结构的复杂性。
-
客户端必须了解不同的策略:客户端需要了解并选择合适的策略,这要求客户端具备一定的策略选择能力。
-
每次改变策略需要重新配置上下文:如果策略需要动态改变,客户端需要频繁地修改上下文对象的策略。
策略模式的应用场景
策略模式适用于以下几种场景:
-
当一个类的行为依赖于其状态时,可以使用策略模式来避免使用大量的
if-else
语句来处理不同状态下的行为。 -
当有多个算法可以互换使用时,可以使用策略模式将每个算法封装成独立的策略类,从而方便算法的替换与扩展。
-
当算法需要在运行时进行选择时,策略模式允许在运行时通过上下文来切换不同的策略,而不需要修改现有的代码。
-
当需要从一个对象中分离算法的实现时,策略模式可以将算法的实现封装在不同的策略类中,从而使得算法与客户端的使用解耦。
策略模式的实现
6.1 基本实现
下面是一个简单的策略模式实现的例子,展示了如何使用策略模式实现不同的折扣策略。
步骤一:定义策略接口
javaCopy Code// IDiscountStrategy.java
public interface IDiscountStrategy {
double applyDiscount(double price);
}
步骤二:实现具体策略类
javaCopy Code// PercentageDiscountStrategy.java
public class PercentageDiscountStrategy implements IDiscountStrategy {
private double percentage;
public PercentageDiscountStrategy(double percentage) {
this.percentage = percentage;
}
@Override
public double applyDiscount(double price) {
return price * (1 - percentage);
}
}
// FlatDiscountStrategy.java
public class FlatDiscountStrategy implements IDiscountStrategy {
private double discountAmount;
public FlatDiscountStrategy(double discountAmount) {
this.discountAmount = discountAmount;
}
@Override
public double applyDiscount(double price) {
return price - discountAmount;
}
}
步骤三:创建上下文类
javaCopy Code// DiscountContext.java
public class DiscountContext {
private IDiscountStrategy discountStrategy;
public DiscountContext(IDiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public double calculateDiscountedPrice(double price) {
return discountStrategy.applyDiscount(price);
}
public void setDiscountStrategy(IDiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
}
步骤四:客户端使用
javaCopy Codepublic class StrategyPatternDemo {
public static void main(String[] args) {
double originalPrice = 100.0;
// 使用百分比折扣策略
DiscountContext context = new DiscountContext(new PercentageDiscountStrategy(0.1));
System.out.println("Price after percentage discount: " + context.calculateDiscountedPrice(originalPrice));
// 切换为固定金额折扣策略
context.setDiscountStrategy(new FlatDiscountStrategy(15.0));
System.out.println("Price after flat discount: " + context.calculateDiscountedPrice(originalPrice));
}
}
输出:
textCopy CodePrice after percentage discount: 90.0 Price after flat discount: 85.0
6.2 策略模式与工厂模式结合
在实际开发中,策略模式经常与工厂模式结合使用,尤其是在策略类的创建较为复杂或数量较多时。工厂模式可以帮助简化策略对象的创建过程,客户端只需要通过工厂方法来获取所需的策略对象。
示例代码:
javaCopy Code// DiscountStrategyFactory.java
public class DiscountStrategyFactory {
public static IDiscountStrategy getStrategy(String strategyType) {
switch (strategyType) {
case "PERCENTAGE":
return new PercentageDiscountStrategy