【设计模式】 模式PK:命令模式VS策略模式

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、总结

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

● 关注点不同

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

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

● 角色功能不同

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

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

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

● 使用场景不同

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

时间: 2024-10-27 10:13:44

【设计模式】 模式PK:命令模式VS策略模式的相关文章

(四)设计模式之PHP项目应用(策略模式:自动驾驶系统)

1 前言 关于策略模式的定义,模式组成,模式核心思想,模式架构图,程序架构等基础知识介绍.请先参考我的另外一篇博客<(三)设计模式之PHP项目应用(策略模式:商场收银系统)>:http://blog.csdn.net/clevercode/article/details/45722661. 2 项目应用 2.1 需求说明 某公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,只用实现启动,转弯,停止功能即可.该系统可以在福特和本田车上使用.这

设计模式学习总结(八)策略模式(Strategy)

策略模式,主要是针对不同的情况采用不同的处理方式.如商场的打折季,不同种类的商品的打折幅度不一,所以针对不同的商品我们就要采用不同的计算方式即策略来进行处理. 一.示例展示: 以下例子主要通过对手机和笔记本添加不同的策略来实现策略模式的应用! 1. 创建抽象策略角色:DailyProductStrategy abstract class DailyProductStrategy { public abstract void AlgorithmInterface(); } 2. 创建具体策略角色:

《大话设计模式学习笔记》2:策略模式

商场促销示例: 1.策略类: public abstract class CashSuper { public abstract double AcceptCash(double money); } 2.具体策略类(以打折类为例): public class CashRebate:CashSuper { private double moneyRebate; public CashRebate(double moneyRebate) { this.moneyRebate = moneyRebat

善用设计模式改善我们丑陋的代码——策略模式

有时候因为种种原因导致我们会写出很多丑陋的代码,比如赶工时,短暂性的偷懒,不会设计模式等等导致代码沉积,一个cs上万行代码这样场景是有发生, 当然这里也包括我...所以时间充裕一点之后就想重构一下,毕竟项目中的需求是不断变更的,面对需求变更,尽量做到最低限度的修改代码,最大化的扩充 新代码,还有一点就是不要过分的追求设计模式,做到适可为止,太设计模式了会导致类太多,不好管理,在项目开发中,其实仔细考虑一下,你会发现很多业 务逻辑都有相应的设计模式帮你优化,毕竟这些都是前辈们踩了无数的坑,经过无数

(三)设计模式之PHP项目应用(策略模式:商场收银系统)

1 策略模式简介 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 2 模式组成 1)抽象策略角色(Strategy): 策略类,通常由一个接口或者抽象类实现. 2)具体策略角色(ConcreteStrategy): 包装了相关的算法和行为. 3)环境角色(Context): 持有一个策略类的引用,最终给客户端调用. 3 模式核心思想 策略模式是一种定义一些列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是

PHP设计模式系列(一):策略模式

策略模式 策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法.比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法.组成 抽象策略角色:策略类,通常由一个接口或者抽象类实现.    具体策略角色:包装了相关的算法和行为.    环境角色:持有一个策略类的引用,最终给客户端调用. 实现结构图 步骤 定义抽象角色类(定义好各个实现的共同抽象方法)    定义具体策略类(具体实现父类的共同方法)    定义环境角色

设计模式学习(十五) 策略模式

策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法,并且由客户端决定调用那个算法 本质: -- 分离算法,选择实现 开发中常见的场景: 代码实现: package strategy; public interface Strategy { public double getPrice(double standardPrice); } 策略接口 package strategy; public class NewCusto

大话设计模式C++实现-第2章-策略模式

一.UML图 二.概述 策略模式:他定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 三.优点 (1)策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合. (2)策略模式的Strategy类曾是为Context定义了一些列的可供重用的算法或行为.集成有助于析取出这些算法中的公共功能. (3)策略模式简化了单元测试,因为每个算法

《大话设计模式》ruby版代码:策略模式

需求: 商场收银软件,根据客户购买物品的单价和数量,计算费用,会有促销活动,打八折,满三百减一百之类的. 一,使用工厂模式. # -*- encoding: utf-8 -*- #现金收费抽象类 class CashSuper def accept_cash(money) end end #正常收费子类 class CashNormal < CashSuper def accept_cash(money) money end end #打折收费子类 class CashRebate < Cas

设计模式学习笔记(四:策略模式)

1.1概述 方法是类中最重要的组成部分,一个方法的方法体由一系列语句构成,也就是说一个方法的方法体是一个算法.在某些设计中,一个类的设计人员经常可能涉及这样的问题:由于用户需求的变化,导致经常需要修改类中某个方法的方法体,即需要不断地变化算法.在这样的情况下可以考虑使用策略模式. 策略模式是处理算法不同变体的一种成熟模式,策略模式通过接口或抽象类封装算法的标识,即在接口中定义一个抽象方法,实现该接口的类将实现接口中的抽象方法.策略模式把针对一个算法标识的一系列具体算法分别封装在不同的类中,使得各