19-05-24
jdon
目的
定义一系列算法,封装每个算法,并使它们可互换。策略模式允许算法独立于使用它的客户端。
解释
为了解释现实世界中的策略,让我们以软件开发人员为例。如果语言不是问题,我可能会要求开发人员为我编写一段代码来创建用户界面。一个开发者选择的语言是Java,所以他将用Swing开发UI。同时,另一个开发人员决定使用C。我不介意,我已经把如何编写UI的细节留给了开发人员,并且他们都应用了自己的策略。在任何阶段,开发人员都可以改变他们的策略,如果他们觉得有必要,可以选择使用不同的语言。所有这些都是关于动态变化的行为。
技术示例
1.策略模式的最佳示例之一是采用Comparator参数的Collections.sort()方法。基于Comparator接口的不同实现,对象将以不同的方式进行排序。
2.策略模式的另一个用途是以不同格式保存文件,运行各种排序算法或文件压缩。
3.要执行诸如加法,减法,除法和乘法等数学运算,我们需要决定在运行时执行哪个运算。
结构
参与者
1.策略(Strategy)
- 声明所有支持的算法共有的接口。
- context使用此接口来调用ConcreteStrategy定义的算法。
2.具体策略 ConcreteStrategy
- 使用Strategy接口实现算法。
3.上下文 (Context)
- 配置了ConcreteStrategy对象。
- 维护对Strategy对象的引用。
- 可以定义一个让Strategy访问其数据的接口。
合作
- 策略和上下文交互以实现所选算法。当调用算法时,上下文可以将算法所需的所有数据传递给策略。或者,上下文可以将自己作为参数传递给策略操作。这使策略可以根据需要调用上下文。
- 上下文将客户的请求转发给其策略。客户端通常创建一个具体的策略对象并将其传递给上下文;此后,客户端专门与上下文进行交互。通常有一系列具体的策略类供客户选择。
让我们看看它是如何在Java中运行的。
1.实现(数学运算作为算法)
请参阅此实现的结构图,并按照结构图创建组件 。
第1步:创建一个类似Strategy.java的界面
public interface Strategy { public int doOperation(int num1, int num2); } |
第2步:创建实现相同接口的具体类。
OperationAdd.java
public class OperationAdd implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 + num2; } } |
OperationSubstract.java
public class OperationSubstract implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 - num2; } } |
OperationMultiply.java
public class OperationMultiply implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 * num2; } } |
operationDevision.java
public class OperationDevision implements Strategy{ @Override public int doOperation(int num1, int num2) { return (num1/num2); } } |
第3步:创建 上下文 类。
Context.java
public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeStrategy(int num1, int num2){ return strategy.doOperation(num1, num2); } } |
第4步:使用 上下文 查看更改其策略时的行为更改 。
StrategyPatternDemo.java
public class StrategyPatternDemo { public static void main(String[] args) { Context context = new Context(new OperationAdd()); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationSubstract()); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationMultiply()); System.out.println("10 * 5 = " + context.executeStrategy(10, 5)); } } |
第5步:验证输出。
10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 |
2.实施(文件压缩工具)
请参阅 此实现的 结构图,并按照结构图创建组件 。让我们使用文件压缩工具的例子 - 我们创建zip或rar文件。首先,我们需要一个策略:
//Strategy Interface public interface CompressionStrategy { public void compressFiles(ArrayList<File> files); } |
我们需要提供两个实现,一个用于zip,一个用于rar
public class ZipCompressionStrategy implements CompressionStrategy { public void compressFiles(ArrayList<File> files) { //using ZIP approach } } |
public class RarCompressionStrategy implements CompressionStrategy { public void compressFiles(ArrayList<File> files) { //using RAR approach } } |
我们的上下文将为客户端提供压缩文件的方法。假设我们的应用程序中有一个首选项设置,用于设置要使用的压缩算法。我们可以使用Context中的setCompressionStrategy方法更改策略。
public class CompressionContext { private CompressionStrategy strategy; //this can be set at runtime by the application preferences public void setCompressionStrategy(CompressionStrategy strategy) { this.strategy = strategy; } //use the strategy public void createArchive(ArrayList<File> files) { strategy.compressFiles(files); } } |
很明显,所有客户端现在要做的就是将文件传递给CompressionContext。
public class Client { public static void main(String[] args) { CompressionContext ctx = new CompressionContext(); //we could assume context is already set by preferences ctx.setCompressionStrategy(new ZipCompressionStrategy()); //get a list of files... ctx.createArchive(fileList); } } |
适用场景
- 许多相关类只在他们的行为上有所不同。策略模式提供了一种将类配置为多种行为之一的方法。
- 你需要不同的算法变体。例如,您可以定义反映不同空间/时间权衡的算法。当这些变体作为算法的类层次结构实现时,可以使用策略模式。
- 算法使用客户端不应该知道的数据。使用策略模式可避免暴露复杂的特定于算法的数据结构。
- 一个类定义了许多行为,这些行为在其操作中显示为多个条件语句。而不是很多条件,将相关的条件分支移动到自己的Strategy类中。