设计模式 - 策略模式
2013-01-15 14:58
267 查看
Strategy Pattern,个人用的最多的一种模式之一。这种模式比较简单,但是却很有效。
意图
定义一系列的算法,把它们一个一个封装起来,并且使它们可以互相替换。本模式使得算法可独立于使用它的客户而变化。
结构
从结构上看策略模式还是蛮简单的,两个参与者:context和strategy。context会将它的客户的请求转发给context的策略对象。从而相应的策略对象可以完成客户的请求。
以前写过一个小小的网络监听软件,用来知道其他应用程序发送了什么文件。我们知道发送文件可以有很多方法:
1. 首先常用的ftp协议;
2. http协议也可以;
3. 还有其他的协议包括一些自定义协议。
网络抓包有很多办法,比如写一个SPI或者TDI驱动。都可以得到其他应用程序发送的数据。但是我们得到的是网络数据,也就是说这些数据会根据使用的协议不同而不同。比如发送一个文件,文件内容是hello world,那么假如使用http协议发送,网络抓包抓下来的数据就会像是:
xxxxx
hello world
xxxxx
文件内容处于数据包中,但是同时这个数据包还有其他的一些数据,比如http头。那么怎么提取出文件内容呢?这个时候就需要一套算法来提取。而且针对不同的协议需要不同的算法。我们可以用策略模式来封装不同协议的算法。
我们以ftp协议和http协议为例。
首先定义strategy基类:
很简单,就提供了一个函数GetFileContent,也就是从输入的网络包数据里面提取文件内容。
看看具体的实现:
FTP和HTTP对应的类可以各自实现各自提取文件内容的算法。
看看context类:
context类里面有一个策略对象,同时我还放了一个cache文件的路径,意思是可以把得到的文件内容存到本地cache文件中。里面有个函数DataReceived(),当监听代码(比如SPI,TDI等)截获一个网络数据包的时候,这个函数会被调用。这样,Context就可以使用相应的策略来分析数据包,从而得到数据包中的文件内容。如:
我们可以看到,要切换策略,相当的简单,只需要创建一个新的策略,并传给context类就可以了。而且算法可以互相替换,因为它们的接口一样。当然使用不正确的策略,会导致得不到正确的结果。比如,我们得到了一些数据,这些数据是从ftp软件过来的,那么就可以用ftp算法来分析。如果发现一种软件不使用http和ftp,而是使用了bt协议,那么可以相应的增加一个strategy子类来处理bt协议。只需要创建一个bt协议的对象,并且传给context就可以了,不会影响到http和ftp。再如果,原来使用的是http1.0协议,现在想升级到http1.1,那么更新http协议strategy类就可以了,不会影响其他的strategy和context。
策略模式的优点:策略可以独立于它的客户而改变,比较容易切换和扩展。如上例中可以增加一个新的协议算法,并且可以很容易地切换不同的算法。
当然策略模式也有缺点:
1. 客户需要知道每个策略,这样才能选择一个正确的策略。往往我们在选择策略的时候还是使用了一个if-else或者switch。
2. 策略类和context类之间的通信,也是一种开销。
其实,策略模式还有个问题,就是谁来创建策略对象和销毁它?这个估计没有固定的套路吧,会有一些选择,比如:
1. 需要的时候创建,用完就销毁。
2. 将策略对象搞成singleton。其实状态模式里面经常可以这么干,但是策略模式的策略对象里面可能会保存一些上下文信息,因为策略对象通常封装了一些算法,可能会涉及一些上下文信息。那么假如搞成singleton的话,就不太合适了,因为策略对象内部的上下文数据可能是会变的。特别是并发的时候,估计会出问题。
3. 使用享元模式,将策略模式放到享元池里面。这样,需要使用策略模式的时候,可以先看看享元池里面有没有空闲的策略对象,如果有,就直接使用,如果没有,则新建一个。
可能还有其他办法,这里只是介绍几种常用的方法。
策略模式和状态模式的区别
如果仔细看策略模式的结构,我们会发现,这个图是不是跟状态模式一模一样啊?是的,确实一模一样。我把这个两种模式的结构图放到了一起,如下图。
哈哈,果然是一模一样。从结构上看,这两种模式是一模一样的,但是它们的意图完全不同:
1. 状态模式强调的是:内部状态的变化可以引起context的行为发生变化。比如例子中的主角可以根据状态的不同,可以有不同的碰撞结果。
2. 策略模式强调的是:Caller可以轻松地使用不同的算法。算法可以独立于客户而改变。
意图
定义一系列的算法,把它们一个一个封装起来,并且使它们可以互相替换。本模式使得算法可独立于使用它的客户而变化。
结构
从结构上看策略模式还是蛮简单的,两个参与者:context和strategy。context会将它的客户的请求转发给context的策略对象。从而相应的策略对象可以完成客户的请求。
以前写过一个小小的网络监听软件,用来知道其他应用程序发送了什么文件。我们知道发送文件可以有很多方法:
1. 首先常用的ftp协议;
2. http协议也可以;
3. 还有其他的协议包括一些自定义协议。
网络抓包有很多办法,比如写一个SPI或者TDI驱动。都可以得到其他应用程序发送的数据。但是我们得到的是网络数据,也就是说这些数据会根据使用的协议不同而不同。比如发送一个文件,文件内容是hello world,那么假如使用http协议发送,网络抓包抓下来的数据就会像是:
xxxxx
hello world
xxxxx
文件内容处于数据包中,但是同时这个数据包还有其他的一些数据,比如http头。那么怎么提取出文件内容呢?这个时候就需要一套算法来提取。而且针对不同的协议需要不同的算法。我们可以用策略模式来封装不同协议的算法。
我们以ftp协议和http协议为例。
首先定义strategy基类:
class CProtocol { public: virtual string GetFileContent(const string& data) = 0; };
很简单,就提供了一个函数GetFileContent,也就是从输入的网络包数据里面提取文件内容。
看看具体的实现:
class CHTTP: public CProtocol { public: //分析传进来的数据data,从中找出发送的文件内容 virtual string GetFileContent(const string& data) { cout << "parse HTTP protocol\n"; return "xxxxxx"; } }; class CFTP: public CProtocol { public: //分析传进来的数据data,从中找出发送的文件内容 virtual string GetFileContent(const string& data) { cout << "parse FTP protocol\n"; return "yyyyy"; } };
FTP和HTTP对应的类可以各自实现各自提取文件内容的算法。
看看context类:
class CProtocolContext { public: CProtocolContext(CProtocol* p, const string& path): m_protocol(p), m_cachepath(path){} //我们的例子是个网络监听软件,当有其他进程发送文件的时候, //这个函数将被调用。这里只是一个模拟 void DataReceived(const string& data) { string content = m_protocol->GetFileContent(data); cout << "save content: \"" << content << "\" to cache file: " << m_cachepath << "\n"; } protected: string m_cachepath; CProtocol* m_protocol; };
context类里面有一个策略对象,同时我还放了一个cache文件的路径,意思是可以把得到的文件内容存到本地cache文件中。里面有个函数DataReceived(),当监听代码(比如SPI,TDI等)截获一个网络数据包的时候,这个函数会被调用。这样,Context就可以使用相应的策略来分析数据包,从而得到数据包中的文件内容。如:
void Pattern_Strategy() { CFTP* ftp = new CFTP(); CProtocolContext* context = new CProtocolContext(ftp, "c:\\cache.txt"); //假设监听代码收到了一些数据 context->DataReceived("abcdaaaaaa"); CHTTP* http = new CHTTP(); context = new CProtocolContext(http, "c:\\cache.txt"); //假设监听代码收到了一些数据 context->DataReceived("abcdaaaaaa"); }
我们可以看到,要切换策略,相当的简单,只需要创建一个新的策略,并传给context类就可以了。而且算法可以互相替换,因为它们的接口一样。当然使用不正确的策略,会导致得不到正确的结果。比如,我们得到了一些数据,这些数据是从ftp软件过来的,那么就可以用ftp算法来分析。如果发现一种软件不使用http和ftp,而是使用了bt协议,那么可以相应的增加一个strategy子类来处理bt协议。只需要创建一个bt协议的对象,并且传给context就可以了,不会影响到http和ftp。再如果,原来使用的是http1.0协议,现在想升级到http1.1,那么更新http协议strategy类就可以了,不会影响其他的strategy和context。
策略模式的优点:策略可以独立于它的客户而改变,比较容易切换和扩展。如上例中可以增加一个新的协议算法,并且可以很容易地切换不同的算法。
当然策略模式也有缺点:
1. 客户需要知道每个策略,这样才能选择一个正确的策略。往往我们在选择策略的时候还是使用了一个if-else或者switch。
2. 策略类和context类之间的通信,也是一种开销。
其实,策略模式还有个问题,就是谁来创建策略对象和销毁它?这个估计没有固定的套路吧,会有一些选择,比如:
1. 需要的时候创建,用完就销毁。
2. 将策略对象搞成singleton。其实状态模式里面经常可以这么干,但是策略模式的策略对象里面可能会保存一些上下文信息,因为策略对象通常封装了一些算法,可能会涉及一些上下文信息。那么假如搞成singleton的话,就不太合适了,因为策略对象内部的上下文数据可能是会变的。特别是并发的时候,估计会出问题。
3. 使用享元模式,将策略模式放到享元池里面。这样,需要使用策略模式的时候,可以先看看享元池里面有没有空闲的策略对象,如果有,就直接使用,如果没有,则新建一个。
可能还有其他办法,这里只是介绍几种常用的方法。
策略模式和状态模式的区别
如果仔细看策略模式的结构,我们会发现,这个图是不是跟状态模式一模一样啊?是的,确实一模一样。我把这个两种模式的结构图放到了一起,如下图。
哈哈,果然是一模一样。从结构上看,这两种模式是一模一样的,但是它们的意图完全不同:
1. 状态模式强调的是:内部状态的变化可以引起context的行为发生变化。比如例子中的主角可以根据状态的不同,可以有不同的碰撞结果。
2. 策略模式强调的是:Caller可以轻松地使用不同的算法。算法可以独立于客户而改变。
相关文章推荐
- 设计模式--策略模式(Strategy Pattern)
- 26种设计模式之策略模式
- 设计模式之十四------Strategy(策略)
- 设计模式之禅学习笔记--策略模式
- Java与设计模式-策略模式
- 设计模式之三——策略模式(泡妞讲策略模式)
- Java设计模式之策略模式(13)
- iOS开发设计策略模式
- 设计模式 (一)——策略模式(Strategy,行为型)
- Android 最常用的设计模式十 安卓源码分析——策略模式(Strategy)
- 理解设计模式之策略模式
- Java设计模式-策略模式(strategy)
- 设计模式---策略模式
- PHP设计模式之:策略模式
- JAVA设计模式(13) —<行为型>策略模式(strategy)
- 设计模式——策略模式(Strategy Pattern)
- python设计模式(二)--策略模式(中)
- 设计模式之策略模式Strategy
- 《Head First 设计模式》例子的C++实现(1 策略模式)