模式开发之旅(18):策略模式
2010-08-20 12:17:37策略模式: 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
另外策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
最有一点就是,在我们开始编程的时候不得不在客户端的代码中为了判断用哪一个算法计算而使用switch条件分支,这也是很正常的。因为,当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
示例
假设有这样一个需求,希望写一个小程序,实现对一个文件进行排序的功能。文件中只包含整型数,并且,相邻的数字通过逗号来区隔。如果由你来编写这样一个小程序,你会如何来实现呢?
你可能会说,这不是很简单嘛,只需要将文件中的内容读取出来,并且通过逗号分割成一个一个的数字,放到内存数组中,然后编写某种排序算法(比如快排),或者直接使用编程语言提供的排序函数,对数组进行排序,最后再将数组中的数据写入文件就可以了。
但是,如果文件很大呢?比如有 10GB 大小,因为内存有限(比如只有 8GB 大小),我们没办法一次性加载文件中的所有数据到内存中,这个时候,我们就要利用外部排序算法。
如果文件更大,比如有 100GB 大小,我们为了利用 CPU 多核的优势,可以在外部排序的基础之上进行优化,加入多线程并发排序的功能。
如果文件非常大,比如有 1TB 大小,即便是单机多线程排序,这也算很慢了。这个时候,我们可以使用真正的 MapReduce 框架,利用多机的处理能力,提高排序的效率。
解决思路讲完了,不难理解。接下来,我们看一下,如何将解决思路翻译成代码实现。
abstract class SortAlg
{
public abstract List<double> Sort(List<double> list);
}
class QuickSort: SortAlg
{
public override List<double> Sort(List<double> list)
{
//快速排序的实现
}
}
class ExternalSort : SortAlg
{
public override List<double> Sort(List<double> list)
{
//外部排序的实现
}
}
class ExternalSort : SortAlg
{
public override List<double> Sort(List<double> list)
{
//外部排序的实现
}
}
class ConcurrentExternalSort : SortAlg
{
public override List<double> Sort(List<double> list)
{
//并发外部排序的实现
}
}
class MapReduceSort : SortAlg
{
public override List<double> Sort(List<double> list)
{
//MapReduce框架排序的实现
}
}
class SortAlgContext
{
private SortAlg algs;
public SortAlgContext(SortAlg algs)
{
this.algs = algs;
}
public List<double> GetSortAlg(List<double> list)
{
return algs.Sort(list);
}
}
//客户端调用
public class Sorter
{
private const long GB = 1000 * 1000 * 1000;
public void SortFile(List<double> list)
{
long fileSize = Size(list); //获取大小的虚拟方法
SortAlgContext context;
if (fileSize < 6 * GB)
context = new SortAlgContext(new QuickSort());
else if (fileSize < 10 * GB)
context = new SortAlgContext(new ExternalSort());
else if (fileSize < 100 * GB)
context = new SortAlgContext(new ConcurrentExternalSort());
else
context = new SortAlgContext(new MapReduceSort());
context.GetSortAlg(list);
}
}
上面示例中客户端需要判断文件大小来选择合适的算法。实际上可以让策略模式与简单工厂结合,把算法的选择交给工厂,简化客户端的调用。重构后的代码如下:
class SortAlgContext
{
private const long GB = 1000 * 1000 * 1000;
List<double> list;
private SortAlg algs;
public SortAlgContext(List<double> list)
{
this.list = list;
long fileSize = Size(list); //获取大小的虚拟方法
if (fileSize < 6 * GB)
algs = new QuickSort();
else if (fileSize < 10 * GB)
algs = new ExternalSort();
else if (fileSize < 100 * GB)
algs = new ConcurrentExternalSort();
else
algs = new MapReduceSort();
}
public List<double> GetSortAlg()
{
return algs.Sort(list);
}
}
public class Sorter
{
public void SortFile(List<double> list)
{
var context = new SortAlgContext(list);
context.GetSortAlg();
}
}