设计模式 - 策略模式

本文详细介绍了策略模式在数据解析场景中的应用,通过封装不同协议的解析算法,实现了算法的独立性和可互换性。以FTP和HTTP协议为例,展示了如何通过策略模式轻松切换解析策略,以及策略模式的优缺点。此外,还探讨了策略模式与其他模式的区别,如状态模式,并提供了策略对象的创建、销毁策略和多种应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Strategy Pattern,个人用的最多的一种模式之一。这种模式比较简单,但是却很有效。

意图

定义一系列的算法,把它们一个一个封装起来,并且使它们可以互相替换。本模式使得算法可独立于使用它的客户而变化。

结构

从结构上看策略模式还是蛮简单的,两个参与者:context和strategy。context会将它的客户的请求转发给context的策略对象。从而相应的策略对象可以完成客户的请求。

以前写过一个小小的网络监听软件,用来知道其他应用程序发送了什么文件。我们知道发送文件可以有很多方法:

1. 首先常用的ftp协议;

2. http协议也可以;

3. 还有其他的协议包括一些自定义协议。

网络抓包有很多办法,比如写一个SPI或者TDI驱动。都可以得到其他应用程序发送的数据。但是我们得到的是网络数据,也就是说这些数据会根据使用的协议不同而不同。比如发送一个文件,文件内容是hello world,那么假如使用http协议发送,网络抓包抓下来的数据就会像是:

xxxxx

hello world

xxxxx

文件内容处于数据包中,但是同时这个数据包还有其他的一些数据,比如http头。那么怎么提取出文件内容呢?这个时候就需要一套算法来提取。而且针对不同的协议需要不同的算法。我们可以用策略模式来封装不同协议的算法。

我们以ftp协议和http协议为例。

首先定义strategy基类:

[cpp]  view plain copy
  1. class CProtocol  
  2. {  
  3. public:  
  4.     virtual string GetFileContent(const string& data) = 0;  
  5. };  

很简单,就提供了一个函数GetFileContent,也就是从输入的网络包数据里面提取文件内容。

看看具体的实现:

[cpp]  view plain copy
  1. class CHTTP: public CProtocol  
  2. {  
  3. public:  
  4.     //分析传进来的数据data,从中找出发送的文件内容  
  5.     virtual string GetFileContent(const string& data)  
  6.     {  
  7.         cout << "parse HTTP protocol\n";  
  8.         return "xxxxxx";  
  9.     }  
  10. };  
  11.   
  12. class CFTP: public CProtocol  
  13. {  
  14. public:  
  15.     //分析传进来的数据data,从中找出发送的文件内容  
  16.     virtual string GetFileContent(const string& data)  
  17.     {  
  18.         cout << "parse FTP protocol\n";  
  19.         return "yyyyy";  
  20.     }  
  21. };  

FTP和HTTP对应的类可以各自实现各自提取文件内容的算法。

看看context类:

[cpp]  view plain copy
  1. class CProtocolContext  
  2. {  
  3. public:  
  4.     CProtocolContext(CProtocol* p, const string& path): m_protocol(p), m_cachepath(path){}  
  5.     //我们的例子是个网络监听软件,当有其他进程发送文件的时候,  
  6.     //这个函数将被调用。这里只是一个模拟  
  7.     void DataReceived(const string& data)  
  8.     {  
  9.         string content = m_protocol->GetFileContent(data);  
  10.         cout << "save content: \"" << content << "\" to cache file: " << m_cachepath << "\n";  
  11.     }  
  12.   
  13. protected:  
  14.     string m_cachepath;  
  15.     CProtocol* m_protocol;  
  16. };  

context类里面有一个策略对象,同时我还放了一个cache文件的路径,意思是可以把得到的文件内容存到本地cache文件中。里面有个函数DataReceived(),当监听代码(比如SPI,TDI等)截获一个网络数据包的时候,这个函数会被调用。这样,Context就可以使用相应的策略来分析数据包,从而得到数据包中的文件内容。如:

[cpp]  view plain copy
  1. void Pattern_Strategy()  
  2. {  
  3.     CFTP* ftp = new CFTP();  
  4.     CProtocolContext* context = new CProtocolContext(ftp, "c:\\cache.txt");  
  5.   
  6.     //假设监听代码收到了一些数据  
  7.     context->DataReceived("abcdaaaaaa");  
  8.   
  9.     CHTTP* http = new CHTTP();  
  10.     context = new CProtocolContext(http, "c:\\cache.txt");  
  11.     //假设监听代码收到了一些数据  
  12.     context->DataReceived("abcdaaaaaa");  
  13. }  

我们可以看到,要切换策略,相当的简单,只需要创建一个新的策略,并传给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可以轻松地使用不同的算法。算法可以独立于客户而改变。


转载:http://blog.youkuaiyun.com/zj510/article/details/8505370

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值