策略模式(Strategy Design Pattern)


目的
定义一系列算法,封装每个算法,并使它们可互换。策略模式允许算法独立于使用它的客户端。

解释
为了解释现实世界中的策略,让我们以软件开发人员为例。如果语言不是问题,我可能会要求开发人员为我编写一段代码来创建用户界面。一个开发者选择的语言是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类中。