简析设计模式之工厂模式与策略模式的实际组合应用
大家打开像QQ电脑管家这类安全软件,主界面上显示的就是体检按钮,点下去体检的项早已经数年前的寥寥之数增加到现在的数十条,而切换到杀毒页面上显示的无论是快速查杀或者全盘扫描也已经从几年前几个盘符+内存变成了现在扫描电脑里面各个关键位置。而对于一个软件设计师而言,我们最早应该如何设计这个扫描策略以保证能够满足后来不断扩充需求呢?
注:本人并没有实际分析过像QQ管家在这上面的设计,只是写出一种自认为可以满足以上需求并且在实际项目中应用过的设计方法。
其实以上需求便是策略模式的实际良好应用,策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。如果以QQ管家体检时检查数字签名服务,扫描host文件,检查开机启动项,扫描内存四项为例,策略模式应用就是将每一项定义为互相独立的算法同时通过抽象接口与客户端连接起来,然后依次调用完成检查功能。相关扫描策略算法定义如下:
1 //扫描策略的基类 2 class CStrategyBase 3 { 4 public: 5 CStrategyBase(){m_bIsSafely = TRUE;} 6 virtual ~CStrategyBase(){}; 7 //定义扫描接口,用于检查 8 virtual BOOL Scan() = 0; 9 //定义修复接口,用于修复该项出现的问题 10 virtual BOOL Repair() = 0; 11 void SetScanResult(){m_bIsSafely = FALSE;} 12 BOOL GetScanResult(){return m_bIsSafely;} 13 private: 14 BOOL m_bIsSafely; 15 }; 16 17 class CScanAutoRun : public CStrategyBase 18 { 19 public: 20 virtual BOOL Scan() 21 { 22 SetScanResult(); 23 printf("CScanHostFile已经检查完毕!"); 24 return TRUE; 25 } 26 27 virtual BOOL Repair() 28 { 29 if (!GetScanResult()) 30 { 31 printf("CScanHostFile已经修复完毕!"); 32 } 33 return TRUE; 34 } 35 }; 36 37 class CScanHostFile : public CStrategyBase 38 { 39 public: 40 virtual BOOL Scan() 41 { 42 SetScanResult(); 43 printf("CScanHostFile已经检查完毕!"); 44 return TRUE; 45 } 46 47 virtual BOOL Repair() 48 { 49 if (!GetScanResult()) 50 { 51 printf("CScanHostFile已经修复完毕!"); 52 } 53 return TRUE; 54 } 55 }; 56 57 class CScanMemFile : public CStrategyBase 58 { 59 public: 60 virtual BOOL Scan() 61 { 62 SetScanResult(); 63 printf("CScanMemFile已经检查完毕!"); 64 return TRUE; 65 } 66 67 virtual BOOL Repair() 68 { 69 if (!GetScanResult()) 70 { 71 printf("CScanMemFile已经修复完毕!"); 72 } 73 74 return TRUE; 75 } 76 };
以上便是基本的扫描策略,所有的扫描算法都封装到类中并且由CStrategyBase,而实际应用则如下所示:
1 vector<CStrategyBase*> vtScanStrategy; 2 vtScanStrategy.reserve(3); 3 4 CStrategyBase *pStrategyBase = NULL; 5 pStrategyBase = new CScanMemFile; 6 vtScanStrategy.push_back(pStrategyBase); 7 pStrategyBase = new CScanHostFile; 8 vtScanStrategy.push_back(pStrategyBase); 9 pStrategyBase = new CScanAutoRun; 10 vtScanStrategy.push_back(pStrategyBase); 11 12 int iSize = vtScanStrategy.size(); 13 for (int i = 0;i < iSize;i++) 14 { 15 CStrategyBase *pStrategyBase = vtScanStrategy[i]; 16 pStrategyBase->Scan(); 17 } 18 19 for (int i = 0;i < iSize;i++) 20 { 21 CStrategyBase *pStrategyBase = vtScanStrategy[i]; 22 delete pStrategyBase; 23 } 24 return 0;
以上只是简单写了一个扫描功能,,如果需要再次增加扫描项时则只需要继续派生出相应的类,然后new出实例再装载到响应的向量组中即可,循环的时候则会自动扫描。但在真正的实际应用时则需添加扫描回调函数以方便显示界面。虽然策略方面已经差不多了,但是以上代码仍然有许多不足,比如说new实例这部分其实可以弄成一个工厂模式,将所有申请和装载操作放在响应的工厂类中。同时工厂模式和策略模式的区别也很明显了,工厂模式类似于生产零件(即这里的实例),但是策略模式却是利用申请出来的实例。倘若是开发类似QQ管家体检功能,设计者只需要专注与扫描和修复算法,至于整个扫描框架根本不需要改动,同时也遵循了设计模式的原则之一开闭原则。