一、 策略(Strategy)模式
策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
假设现在要设计一个贩卖各类书籍的电子商务网站的购物车(Shopping Cat)系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的教材类图书实行每本一元的折扣;对连环画类图书提供每本7%的促销折扣,而对非教材类的计算机图书有3%的折扣;对其余的图书没有折扣。由于有这样复杂的折扣算法,使得价格计算问题需要系统地解决。
使用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,各种算法则在具体策略类(ConcreteStrategy)中提供。由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。当出现新的促销折扣或现有的折扣政策出现变化时,只需要实现新的策略类,并在客户端登记即可。策略模式相当于"可插入式(Pluggable)的算法"。
二、 策略模式的结构
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:"准备一组算法,并将每一个算法封装起来,使得它们可以互换。"
策略又称做政策(Policy)模式【GOF95】。下面是一个示意性的策略模式结构图:
这个模式涉及到三个角色:
· 环境(Context)角色:持有一个Strategy类的引用。
· 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
· 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
三、 示意性源代码
// Strategy pattern -- Structural example
using System;
// "Strategy"
abstract class Strategy
{
// Methods
abstract public void AlgorithmInterface();
}
// "ConcreteStrategyA"
class ConcreteStrategyA : Strategy
{
// Methods
override public void AlgorithmInterface()
{
Console.WriteLine("Called ConcreteStrategyA.AlgorithmInterface()");
}
}
// "ConcreteStrategyB"
class ConcreteStrategyB : Strategy
{
// Methods
override public void AlgorithmInterface()
{
Console.WriteLine("Called ConcreteStrategyB.AlgorithmInterface()");
}
}
// "ConcreteStrategyC"
class ConcreteStrategyC : Strategy
{
// Methods
override public void AlgorithmInterface()
{
Console.WriteLine("Called ConcreteStrategyC.AlgorithmInterface()");
}
}
// "Context"
class Context
{
// Fields
Strategy strategy;
// Constructors
public Context( Strategy strategy )
{
this.strategy = strategy;
}
// Methods
public void ContextInterface()
{
strategy.AlgorithmInterface();
}
}
/**//// <summary>
/// Client test
/// </summary>
public class Client
{
public static void Main( string[] args )
{
// Three contexts following different strategies
Context c = new Context( new ConcreteStrategyA() );
c.ContextInterface();
Context d = new Context( new ConcreteStrategyB() );
d.ContextInterface();
Context e = new Context( new ConcreteStrategyC() );
e.ContextInterface();
}
}
四、 何时使用何种具体策略角色
在学习策略模式时,学员常问的一个问题是:为什么不能从策略模式中看出哪一个具体策略适用于哪一种情况呢?
答案非常简单,策略模式并不负责做这个决定。换言之,应当由客户端自己决定在什么情况下使用什么具体策略角色。策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中"退休"的方便,策略模式并不决定在何时使用何种算法。
五、 一个实际应用策略模式的例子
下面的例子利用策略模式在排序对象中封装了不同的排序算法,这样以便允许客户端动态的替换排序策略(包括Quicksort、Shellsort和Mergesort)。
// Strategy pattern -- Real World example
using System;
using System.Collections;
// "Strategy"
abstract class SortStrategy
{
// Methods
abstract public void Sort( ArrayList list );
}
// "ConcreteStrategy"
class QuickSort : SortStrategy
{
// Methods
public override void Sort(ArrayList list )
{
list.Sort(); // Default is Quicksort
Console.WriteLine("QuickSorted list ");
}
}
// "ConcreteStrategy"
class ShellSort : SortStrategy
{
// Methods
public override void Sort(ArrayList list )
{
//list.ShellSort();
Console.WriteLine("ShellSorted list ");
}
}
// "ConcreteStrategy"
class MergeSort : SortStrategy
{
// Methods
public override void Sort( ArrayList list )
{
//list.MergeSort();
Console.WriteLine("MergeSorted list ");
}
}
// "Context"
class SortedList
{
// Fields
private ArrayList list = new ArrayList();
private SortStrategy sortstrategy;
// Constructors
public void SetSortStrategy( SortStrategy sortstrategy )
{
this.sortstrategy = sortstrategy;
}
// Methods
public void Sort()
{
sortstrategy.Sort( list );
}
public void Add( string name )
{
list.Add( name );
}
public void Display()
{
foreach( string name in list )
Console.WriteLine( " " + name );
}
}
/**//// <summary>
/// StrategyApp test
/// </summary>
public class StrategyApp
{
public static void Main( string[] args )
{
// Two contexts following different strategies
SortedList studentRecords = new SortedList( );
studentRecords.Add( "Samual" );
studentRecords.Add( "Jimmy" );
studentRecords.Add( "Sandra" );
studentRecords.Add( "Anna" );
studentRecords.Add( "Vivek" );
studentRecords.SetSortStrategy( new QuickSort() );
studentRecords.Sort();
studentRecords.Display();
}
}
六、 在什么情况下应当使用策略模式
在下面的情况下应当考虑使用策略模式:
1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2. 一个系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法类的对象。
3. 一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和只与算法有关的数据。
4. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念。
七、 策略模式的优点和缺点
策略模式有很多优点和缺点。它的优点有:
1. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免重复的代码。
2. 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
3. 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
策略模式的缺点有:
1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2. 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。
八、 其它
策略模式与很多其它的模式都有着广泛的联系。Strategy很容易和Bridge模式相混淆。虽然它们结构很相似,但它们却是为解决不同的问题而设计的。Strategy模式注重于算法的封装,而Bridge模式注重于分离抽象和实现,为一个抽象体系提供不同的实现。Bridge模式与Strategy模式都很好的体现了"Favor composite over inheritance"的观点。
推荐大家读一读《IoC 容器和Dependency Injection 模式》,作者Martin Fowler。网上可以找到中文版的PDF文件。为策略模式的实施提供了一个非常好的方案。
分享到:
相关推荐
设计模式(22)-Strategy Pattern 设计模式(21)-Template Method Pattern 设计模式(20)-Visitor Pattern 设计模式(19)-Observer Pattern 设计模式(18)-Command Pattern 设计模式(17)-Chain of ...
【标题】"sprintboot-strategy-pattern-demo-master.zip" 是一个使用Spring Boot技术实现的策略模式示例项目,它提供了一种快速理解并应用策略模式的开箱即用的体验。 【描述】"基于springboot做了一个策略模式的...
策略模式(Strategy Pattern)是一种行为设计模式,它使你能在运行时改变对象的行为。在软件开发中,我们经常遇到需求的变化,特别是在涉及行为或者算法的时候。策略模式提供了一种方式来封装一系列相关的算法,并使...
策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件开发中,我们经常遇到需要根据不同的条件或时间来改变算法的情况。策略模式提供了一种将算法封装到独立的对象中,使得它们可以互换使用的机制,...
1. 策略模式(Strategy Pattern)是一种行为设计模式,允许在运行时选择算法的行为。策略模式的意图是定义一系列算法,将每个算法封装起来,并使它们可以互换。策略模式让算法的变化独立于使用算法的客户端。在提供...
实现对象的复用——享元模式(二) 实现对象的复用——享元模式(三) 实现对象的复用——享元模式(四) 实现对象的复用——享元模式(五) 代理模式-Proxy Pattern 设计模式之代理模式(一) 设计模式之代理模式...
策略模式是一种设计模式,它使你能在运行时改变对象的行为,通过将算法封装在独立的策略类中,实现了策略的动态选择。这种模式的核心在于,它允许程序在不修改现有代码的情况下扩展功能,遵循“开闭原则”。 在上述...
"Design Pattern"这个压缩包文件很可能包含了一些关于设计模式的实例和文档,特别是提到了"design-pattern\doc\api"目录下的"index.html",这可能是一个交互式的文档或者教程,通过实例帮助开发者更好地理解设计模式...
这里我们关注的是一个名为"pattern.zip"的压缩包文件,它包含了23种经典的设计模式,这些模式在实践中被广泛使用,提高了代码的可读性、可维护性和可扩展性。这篇文章将详细探讨这些设计模式及其应用。 首先,23种...
策略模式是一种行为设计模式,允许我们在运行时动态地改变对象的行为。在Django中,模型字段通常有固定的行为,例如验证、默认值处理等。`django-strategy-field`库通过此模式,使得我们可以根据不同的业务需求,为...
在"learn-strategy-pattern-in-unity-in-less-than-15-minutes-master"这个项目中,我们可能会看到以下关键组件: 1. **策略接口(Strategy Interface)**:定义了所有策略类必须实现的方法,例如`MoveStrategy`,...
本资料包“DesignPattern - DesignPattern.zip”提供了对C++11中23种设计模式的全面讲解,特别是结合指针使用的部分,以下是对这些知识点的详细阐述: 1. **单例模式(Singleton)**:确保一个类只有一个实例,并...
策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件开发中,我们经常遇到需要根据不同的条件或时间点执行不同算法的情况。策略模式提供了一种灵活的方式来处理这种情况,通过将每种算法封装为一个...
在"Design Pattern - Strategy Design Pattern _ Model"这个主题中,可能包含了对策略模式的深入讲解和实例应用。文件"T23_设计模式_策略模式.ppt"可能是一个演示文稿,详细介绍了策略模式的原理、结构、优缺点以及...
在众多的设计模式中,策略模式(Strategy Pattern)是一种行为设计模式,它使你能在运行时改变对象的行为。在这个“JAVA设计模式例程-策略模式”的压缩包中,我们可以深入探讨策略模式的原理及其应用。 策略模式的...
策略模式是一种行为设计模式,它使得算法可以在运行时被更改。这种模式允许一个类的行为或其算法在运行时根据需要进行改变,通过这种方式,我们可以轻松地扩展不同的算法而不修改现有代码。 #### 主要问题 在软件...
设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的模板或蓝图,可以被反复使用,以提高代码的可读性、可维护性和可复用性。本资料包“DesignPattern.zip”包含了Java、C++和Golang三种编程语言...
策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件设计中,我们经常遇到需要在不同时间或根据不同条件使用不同算法的情况。策略模式允许我们将算法封装到独立的可互换的策略类中,从而使它们可以...
在这个名为“aula-design-pattern-strategy”的项目中,我们可以看到如何在实际的Java代码中应用策略模式。项目可能包含以下内容: 1. **策略接口**:通常命名为`Strategy`,它定义了业务逻辑的关键操作。例如,一...