65.9K
CodeProject 正在变化。 阅读更多。
Home

理解和实现策略模式的绝对初学者教程 (C#)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (25投票s)

2016 年 1 月 18 日

CPOL

5分钟阅读

viewsIcon

24360

downloadIcon

272

本文的目的是理解策略模式的基础知识。

引言

本文的目的是理解策略模式的基础知识,并尝试了解何时可以使用它,并提供一个基本的实现以便更好地理解。这绝不是在实际应用程序中实现策略模式的方法,所举的例子也与实际相去甚远。本文的想法仅仅是为了阐述策略模式的概念。

背景

在应用程序开发中,有很多场景可以通过多种方式完成相同的操作。我们希望我们的应用程序能够使用所有这些执行操作的方式。一个例子是在电子商务门户上付款,我既可以选择使用网上银行支付,也可以选择使用信用卡,甚至可以选择 PayPal 来进行支付。所有这些都是执行相同操作的方式。虽然每种选择都必须遵循不同的应用程序逻辑,即分开的代码。

另一个可以在多种方式下进行相同操作的例子是排序。我可以使用任何一种排序算法来对一个序列进行排序。因此,当我的情况是我想以这样的方式开发应用程序,以便用户可以为任何操作选择许多可用选项中的一个,那么这可能是引入策略模式的正确位置。

策略模式的理念是为某个操作提供多种策略,并让用户选择(或基于输入数据的某种算法)合适的策略来执行该操作。GoF 将策略模式定义为:“定义一组算法,将每种算法封装起来,并使它们可以相互替换。策略模式使得算法可以独立于使用它的客户端而变化。”

让我们尝试理解这个类图的每个组件。

  • Strategy:这是所有算法的通用接口。Context 使用此接口执行操作。
  • ConcreteStrategy:这是实现实际算法的类。
  • Context:这是客户端应用程序,它负责决定使用哪种策略,并使用 Strategy 接口(它引用一个 ConcreteStrategy 对象)来执行操作。

使用代码

因此,为了理解这些概念,让我们通过一个将音频文件从 Wav 转换为 MP3 的玩具应用程序来工作。在这里,用户可以选择源 .wav 文件,然后选择要创建的目标 MP3 文件的质量。用户界面将为用户提供目标文件的三种质量值。

在我们的代码中,我们将创建多个策略,每个策略都用于定义输出的质量。我们正在实现所有这些独立的策略,以便转换代码将不受处理输出质量的特定代码的影响。执行转换的实际代码将独立于这些不同策略的实现细节,并且对于任何选定的策略,甚至在添加新策略时,它都将以相同的方式工作。

注意:这不是一个真实的转换应用程序,只是一个演示应用程序,但也许真实的应用程序可以在同一条线上开发。另外,应用程序和设计的选择完全是为了演示正在运行的策略模式(如果从整体来看,这可能是一个糟糕的设计)。

因此,让我们先编写 `IWavToMp3ConvertionStrategy` 接口,所有具体的策略类都将实现它。

interface IWavToMp3ConvertionStrategy
{
	void Convert();
}

一旦我们准备好了接口,我们就可以编写具体的策略类。

//Strategy class for low quality conversion
class LowQualityConversionStrategy : IWavToMp3ConvertionStrategy
{
	public void Convert()
	{
		Console.WriteLine("Low quality conversion performed");
	}
}

//Strategy class for average quality conversion
class AverageQualityConversionStrategy : IWavToMp3ConvertionStrategy
{
	public void Convert()
	{
		Console.WriteLine("Average quality conversion performed");
	}
}

//Strategy class for high quality conversion
class HighQualityConversionStrategy : IWavToMp3ConvertionStrategy
{
	public void Convert()
	{
		Console.WriteLine("High quality conversion performed");
	}
}

我们的具体类包含所有策略之间不同的逻辑。所有通用的转换功能逻辑将存在于选择实际转换策略的类中,即上面图中的上下文类。因此,让我们编写 Context 类,即 `WavToMP3Convertor`,它将使用 `IWavToMp3ConvertionStrategy` 接口来执行转换。

public class WavToMP3Convertor
{
	AudioFile m_fileData = null;
	IWavToMp3ConvertionStrategy m_Convertor = null;

	public WavToMP3Convertor(AudioFile fileData)
	{
		m_fileData = fileData;            
	}

	public void Convert(IWavToMp3ConvertionStrategy convertor)
	{
		m_Convertor = convertor;
		m_Convertor.Convert();            
	}
}

这个类中所发生的是,表示层将实际文件数据传递给这个类。然后,当请求转换时,一个具体的策略实例也将被传递给这个类,以便这个类可以使用传递的策略进行转换。

在这段代码中需要注意的重要一点是,即使我们添加更多策略,这段代码也不会受到影响。此外,如果我们创建一个 SDK/DLL,用户将可以选择创建自己的策略,并简单地将它们传递给这个类来使用他们的自定义策略。

最后,我们将拥有我们的表示层,它将允许用户决定转换策略并将适当的策略传递给转换类。

static void Main(string[] args)
{
	IWavToMp3ConvertionStrategy selectedStrategy = null;

	Console.WriteLine("Assuming the file for conversion has been selected already");
	AudioFile file = new AudioFile { Title = "Sample File" };

	// Let us try to emulate the selection of quality here
	Console.WriteLine("Enter the type of output \n1. Low Quality\n2. Average Quality\n3. High Quality");
				
	int choice = Console.Read();

	// Now based on the users' choice lets go ahead and select strategy to convert the file
	if (choice == '1')
	{
		selectedStrategy = new LowQualityConversionStrategy();                
	}
	else if (choice == '2')
	{
		selectedStrategy = new AverageQualityConversionStrategy();                
	}
	else if (choice == '3')
	{
		selectedStrategy = new HighQualityConversionStrategy();                
	}

	// Now the code which is doing the conversion. this code beed 
	// not be changes even if we implement more strategies
	if (selectedStrategy != null)
	{
		WavToMP3Convertor convertor = new WavToMP3Convertor(file);
		convertor.Convert(selectedStrategy);
	}
}

注意:在这个示例应用程序中,策略的选择是在表示层完成的。这是一种相当牵强的创建具体策略的方式。在实际世界中,具体策略将根据用户在 UI 上的选择,使用工厂或服务定位器来创建。

现在,当我们运行应用程序时,我们可以看到文件转换将根据用户选择的选项进行,并使用正确的策略。现在让我们尝试将我们的代码与 GoF 类图进行比较。

因此,我们可以看到 `IWavToMp3ConvertionStrategy` 是我们的策略接口,`WavToMP3Convertor` 是我们的上下文类,而 `LowQualityConversionStrategy`、`AverageQualityConversionStrategy` 和 `HighQualityConversionStrategy` 是我们的具体策略。需要注意的重要一点是,Context 类,即 `WavToMP3Convertor`,将不受具体策略的影响,添加/删除策略不会对该类产生任何影响。

关注点

在本文中,我们了解了策略模式的基础知识以及如何使用此模式将客户端代码与实际实现解耦。我们使用了一个相当牵强的例子来演示这个模式。具体策略的创建也没有使用 switch 语句,但这里的目的是展示 Context 类如何不受策略选择的影响,从而以这种简单的方式创建具体策略。本文是从绝对初学者的角度撰写的。希望它能提供信息。

历史

  • 2016年1月18日 - 初版
© . All rights reserved.