1、概述

  命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色。它们虽然同为行为类模式,但是两者的区别还是很明显的。策略模式的意图是封装算法,它认为“算法”已经是一个完整的、不可拆分的原子业务(注意这里是原子业务,而不是原子对象),即其意图是让这些算法独立,并且可以相互替换,让行为的变化独立于拥有行为的客户;而命令模式则是对动作的解耦,把一个动作的执行分为执行对象(接收者角色)、执行行为(命令角色),让两者相互独立而不相互影响。

  我们从一个相同的业务需求出发,按照命令模式和策略模式分别设计出一套实现,来看看它们的侧重点有什么不同。zip和gzip文件格式相信大家都很熟悉,它们是两种不同的压缩格式,我们今天就来对一个目录或文件实现两种不同的压缩方式:zip压缩和gzip压缩(这里的压缩指的是压缩和解压缩两种对应的操作行为,下同)。

2、策略模式实现压缩算法

2.1 类图

  使用策略模式实现压缩算法非常简单,也是非常标准的。

  在类图中,我们的侧重点是zip压缩算法和gzip压缩算法可以互相替换,一个文件或者目录可以使用zip压缩,也可以使用gzip压缩,选择哪种压缩算法是由高层模块(实际操作者)决定的。

2.2 代码

2.2.1 抽象的压缩算法

  我们来看一下代码实现。先看抽象的压缩算法。

class CIAlgorithm
{
public:
CIAlgorithm(){};
~CIAlgorithm(){}; //压缩算法
bool mbCompress(const string &sSource, const string &sTo);
//解压缩算法
bool mbUncompress(const string &sSource, const string &sTo);
};

  每一个算法要实现两个功能:压缩和解压缩,传递进来一个绝对路径source,compress把它压缩到to目录下,uncompress则进行反向操作——解压缩,这两个方法一定要成对地实现,为什么呢?用gzip解压缩算法能解开zip格式的压缩文件吗?

2.2.2 zip压缩算法

class CZip : public CIAlgorithm
{
public:
CZip(){};
~CZip(){}; //zip格式的压缩算法
bool mbCompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",ZIP压缩成功!" << endl;
return true;
} // zip格式的解压缩算法
bool mbUncompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",ZIP解压缩成功!" << endl;
return true;
}
};

2.2.3 gzip压缩算法

class CGzip : public CIAlgorithm
{
public:
CGzip(){};
~CGzip(){}; //gzip的压缩算法
bool mbCompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",GZIP压缩成功!" << endl;
return true;
} // gzip解压缩算法
bool mbUncompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",GZIP解压缩成功!" << endl;
return true;
}
};

2.2.4 环境角色

  这两种压缩算法实现起来都很简单,两个具体的算法实现了同一个接口,完全遵循依赖倒转原则。我们再来看环境角色。

class CContext
{
public:
//构造函数传递具体的算法
CContext(CIAlgorithm *opAlgorithm){ mopAlgotithm = opAlgorithm; };
~CContext(){}; // 执行压缩算法
bool mbCompress(const string &sSource, const string &sTo)
{
this->mopAlgotithm->mbCompress(sSource, sTo);
return true;
} //执行解压缩算法
bool mbUncompress(const string &sSource, const string &sTo)
{
this->mopAlgotithm->mbUncompress(sSource, sTo);
return true;
} private:
//指向抽象算法
CIAlgorithm *mopAlgotithm;
};

  也是非常简单,指定一个算法,执行该算法,一个标准的策略模式就编写完毕了。

2.2.5 调用

  请注意,这里虽然有两个算法Zip和Gzip,但是对调用者来说,这两个算法没有本质上的区别,只是“形式”上不同,什么意思呢?从调用者来看,使用哪一个算法都无所谓,两者完全可以互换,甚至用一个算法替代另外一个算法。

int main()
{
//对文件执行zip压缩算法
CContext *op_context = new CContext(new CZip); //执行压缩算法
op_context->mbCompress("c:\\windows", "d:\\windows.zip"); //执行解压缩算法
op_context->mbUncompress("c:\\windows.zip", "d:\\windows"); return 0;
}

2.2.6 执行结果

  要使用gzip算法吗?在用户调用换成new CGzip可以了,其他的模块根本不受任何影响,策略模式关心的是算法是否可以相互替换。策略模式虽然简单,但是在项目组使用得非常多,可以说随手拈来就是一个策略模式。

3、命令模式实现压缩算法

3.1 类图

  命令模式的主旨是封装命令,使请求者与实现者解耦。例如,到饭店点菜,客人(请求者)通过服务员(调用者)向厨师(接收者)发送了订单(行为的请求),该例子就是通过封装命令来使请求者和接收者解耦。我们继续来看压缩和解压缩的例子,怎么使用命令模式来完成该需求呢?我们先画出类图。

  类图看着复杂,但是还是一个典型的命令模式,通过定义具体命令完成文件的压缩、解压缩任务,注意我们这里对文件的每一个操作都是封装好的命令,对于给定的请求,命令不同,处理的结果当然也不同,这就是命令模式要强调的。

3.2 代码

3.2.1 抽象命令

class CCMd
{
public:
virtual bool mbExecute(const string &sSource, const string &sTo) = 0; protected:
CCMd()
{
mopZip = new CZipReceiver;
mopGzip = new CGzipReceiver;
} ~CCMd(){};
protected:
CIReceiver *mopZip;
CIReceiver *mopGzip;
};

  抽象命令定义了两个接收者的引用:zip接收者和gzip接收者,它们完全是受众,人家让它干啥它就干啥,具体使用哪个接收者是命令决定的。具体命令有4个:zip压缩、zip解压缩、gzip压缩、gzip解压缩。

3.2.2 zip压缩命令

class CZipCompressCmd : public CCMd
{
public:
CZipCompressCmd(){};
~CZipCompressCmd(){}; bool mbExecute(const string &sSource, const string &sTo) { return mopZip->mbCompress(sSource, sTo); }
};

3.2.3 zip解压缩命令

class CZipUncompressCmd : public CCMd
{
public:
CZipUncompressCmd(){};
~CZipUncompressCmd(){}; bool mbExecute(const string &sSource, const string &sTo) { return mopZip->mbUncompress(sSource, sTo); }
};

3.2.4 gzip压缩命令

class CGzipCompressCmd : public CCMd
{
public:
CGzipCompressCmd(){};
~CGzipCompressCmd(){}; bool mbExecute(const string &sSource, const string &sTo) { return mopGzip->mbCompress(sSource, sTo); }
};

3.2.5 gzip解压缩命令

class CGzipUncompressCmd : public CCMd
{
public:
CGzipUncompressCmd(){};
~CGzipUncompressCmd(){}; bool mbExecute(const string &sSource, const string &sTo) { return mopGzip->mbUncompress(sSource, sTo); }
};

  它们非常简单,都只有一个方法,坚决地执行命令,使用了委托的方式,由接收者来实现。

3.2.6 抽象接收者

class CIReceiver
{
public:
CIReceiver(){};
~CIReceiver(){}; //压缩
virtual bool mbCompress(const string &sSource, const string &sTo) = 0; //解压缩
virtual bool mbUncompress(const string &sSource, const string &sTo) = 0;
};

  抽象接收者与策略模式的抽象策略完全相同,具体的实现也完全相同,只是类名做了改动。

3.2.7 zip接收者

class CZipReceiver :public CIReceiver
{
public:
CZipReceiver(){};
~CZipReceiver(){}; //zip格式的压缩算法
bool mbCompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",ZIP压缩成功!" << endl;
return true;
} //zip格式的解压缩算法
bool mbUncompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",ZIP解压缩成功!" << endl;
return true;
}
};

  这就是一个具体动作执行者,它在策略模式中是一个具体的算法,关心的是是否可以被替换;而在命令模式中,它则是一个具体、真实的命令执行者。

3.2.8 gzip接收者

class CGzipReceiver :public CIReceiver
{
public:
CGzipReceiver(){};
~CGzipReceiver(){}; //zip格式的压缩算法
bool mbCompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",GZIP压缩成功!" << endl;
return true;
} //zip格式的解压缩算法
bool mbUncompress(const string &sSource, const string &sTo)
{
cout << sSource.c_str() << "-->" << sTo.c_str() << ",GZIP解压缩成功!" << endl;
return true;
}
};

3.2.9 调用者

  命令、 接收者都具备了, 我们再来封装一个命令的调用者。

class CInvoker
{
public:
CInvoker(CCMd *opCmd) { mopCmd = opCmd; }
~CInvoker(){}; //执行命令
bool mbExecute(const string &sSource, const string &sTo)
{
mopCmd->mbExecute(sSource, sTo);
return true;
} private:
//抽象命令的引用
CCMd *mopCmd;
};

  调用者非常简单,只负责把命令向后传递,当然这里也可以进行一定的拦截处理,我们暂时用不到就不做处理了。

3.2.10 场景调用

int main()
{
//定义一个命令,压缩一个文件
CCMd *op_cmd = new CZipCompressCmd; //定义调用者
CInvoker *op_invoker = new CInvoker(op_cmd); cout << "========执行压缩命令========" << endl;
op_invoker->mbExecute("c:\\windows", "d:\\windows.zip"); return 0;
}

3.2.11 执行结果

3.3 小结

  想新增一个命令?当然没有问题,只要重新定义一个命令就成,命令改变了,高层模块只要调用它就成。请注意,这里的程序还有点欠缺,没有与文件的后缀名绑定,不应该出现使用zip压缩命令产生一个.gzip后缀的文件名,读者在实际应用中可以考虑与文件后缀名之间建立关联。

  通过以上例子,我们看到命令模式也实现了文件的压缩、解压缩的功能,它的实现是关注了命令的封装,是请求者与执行者彻底分开,看看我们的程序,执行者根本就不用了解命令的具体执行者,它只要封装一个命令——“给我用zip格式压缩这个文件”就可以了,具体由谁来执行,则由调用者负责,如此设计后,就可以保证请求者和执行者之间可以相互独立,各自发展而不相互影响。

  同时,由于是一个命令模式,接收者的处理可以进行排队处理,在排队处理的过程中,可以进行撤销处理,比如客人点了一个菜,厨师还没来得及做,那要撤回很简单,撤回也是命令,这是策略模式所不能实现的。

4、总结

  命令模式和策略模式的类图完全一样,代码实现也比较类似,但是两者还是有区别的。

  ● 关注点不同

  策略模式关注的是算法替换的问题,一个新的算法投产,旧算法退休,或者提供多种算法由调用者自己选择使用,算法的自由更替是它实现的要点。换句话说,策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其可以自由切换。

  命令模式则关注的是解耦问题,如何让请求者和执行者解耦是它需要首先解决的,解耦的要求就是把请求的内容封装为一个一个的命令,由接收者执行。由于封装成了命令,就同时可以对命令进行多种处理,例如撤销、记录等。

  ● 角色功能不同
  在我们的例子中,策略模式中的抽象算法和具体算法与命令模式的接收者非常相似,但是它们的职责不同。策略模式中的具体算法是负责一个完整算法逻辑,它是不可再拆分的原子业务单元,一旦变更就是对算法整体的变更。

  而命令模式则不同,它关注命令的实现,也就是功能的实现。例如我们在分支中也提到接收者的变更问题,它只影响到命令族的变更,对请求者没有任何影响,从这方面来说,接收者对命令负责,而与请求者无关。命令模式中的接收者只要符合六大设计原则,完全不用关心它是否完成了一个具体逻辑,它的影响范围也仅仅是抽象命令和具体命令,对它的修改不会扩散到模式外的模块。

  当然,如果在命令模式中需要指定接收者,则需要考虑接收者的变化和封装,例如一个老顾客每次吃饭都点同一个厨师的饭菜,那就必须考虑接收者的抽象化问题。

  ● 使用场景不同
  策略模式适用于算法要求变换的场景,而命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令多撤销的场景。

模式PK:命令模式VS策略模式的更多相关文章

  1. 策略模式(Strategy)

    行为型模式:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式 策略模式(Strategy) 策略模式定义了一系列算法,并将 ...

  2. 策略模式设计模式(Strategy)摘录

    23种子GOF设计模式一般分为三类:创建模式.结构模型.行为模式. 创建模式抽象的实例.一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将 ...

  3. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  4. IOS之Objective-C学习 策略模式

    对于策略模式,我个人理解策略模式就是对各种规则的一种封装的方法,而不仅仅是对算法的封装与调用而已.与工厂模式中简单工厂有点类似,但是比简单工厂更有耦合度,因为策略模式以相同的方法调用所有的规则,减少了 ...

  5. C#设计模式系列:策略模式(Strategy)

    1.策略模式简介 1.1>.定义 策略是为达到某一目的而采取的手段或方法,策略模式的本质是目标与手段的分离,手段不同而最终达成的目标一致.客户只关心目标而不在意具体的实现方法,实现方法要根据具体 ...

  6. 简单工厂模式和策略模式结合使用php

    策略模式是有客户端自行实例化算法类的,而简单工厂模客户端只传参数,不关心对象的生成. 结合两种模式,可以在使用策略模式的时候客户端不再生成算法的对象.修改策略模式的配置类即可. 在之前策略模式基础上, ...

  7. java设计模式--策略模式

    策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 本文地址:http:// ...

  8. 说说设计模式~策略模式(Strategy)

    返回目录 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.而对于客户端(UI)来说,可以通过IOC再配合工厂模块,实现动态策略的切换,策略模块通常于一个抽象策略对象(in ...

  9. javascript设计模式与开发实践阅读笔记(5)——策略模式

    策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 我的理解就是把各种方法封装成函数,同时存在一个可以调用这些方法的公共函数.这样做的好处是可以消化掉内部的分支判断,使代码效率 ...

  10. C++ 之 策略模式

    1  会飞的鸭子 Duck 基类,包含两个成员函数 swim() 和 display():派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写 display() ...

随机推荐

  1. PHP 文件操作代码

    <?php //echo filetype("./1.jpg"); //判断文件类型 文件:file //echo filetype("./code"); ...

  2. javascript 高级程序设计 四

    新的一天开始,让我们伴随者轻快的心情,开始今天的笔记 1.操作符: (1): *./.-在ECMAScript中操作的时候,如果遇到有一个操作值不是数值型(Number),那么就会在后台调用numbe ...

  3. vue 动态路由按需加载的三种方式

    在Vue项目中,一般使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,如: import Hello from '@/components/Hell ...

  4. Luogu 1169 [ZJOI2007]棋盘制作 - 动态规划+单调栈

    Description 给一个01矩阵, 求出最大的01交错的正方形和最大的01交错的矩阵 Solution 用动态规划求出最大的正方形, 用单调栈求出最大的矩阵. 在这里仅介绍求出最大正方形(求最大 ...

  5. C++基础教程(总结)

    内容中包含 base64string 图片造成字符过多,拒绝显示

  6. LibreOJ #6013. 「网络流 24 题」负载平衡 最小费用最大流 供应平衡问题

    #6013. 「网络流 24 题」负载平衡 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

  7. 10个办法让设计小白迅速get海报设计要点!

    对于设计师而言,海报和宣传单的设计,几乎是每一个设计师的必修课.如今网页上的 Banner.宣传和促销的数字海报,大多脱胎于我们所熟知的海报设计. 对于推销产品.连接客户,海报始终是一种最为有趣和实用 ...

  8. Tomcat的下载、安装、启动与关闭

    ubuntu server 16.04 从官网下载 Binary Distributions 版本的相应的压缩包, https://tomcat.apache.org/download-90.cgi ...

  9. java 银联接口开发

    http://blog.sina.com.cn/s/blog_6c868c470100ys59.html 在线接口文档:http://wenku.baidu.com/link?url=EUgAuOKz ...

  10. [JAVA]JAVA章1 数组数据去重

    一 利用HashSet进行去重 //定义一个数组:有几个重复项 int[] testarray = {1,2,33,4,2,3,44,5,222,3}; //利用HashSet对数组数据去重 Set& ...