zoukankan      html  css  js  c++  java
  • 【设计模式】 模式PK:包装模式群PK

    1、概述

      我们讲了这么多的设计模式,大家有没有发觉在很多的模式中有些角色是不干活的?它们只是充当黔首作用,你有问题,找我,但我不处理,我让其他人处理。最典型的就是代理模式了,代理角色接收请求然后传递到被代理角色处理。门面模式也是一样,门面角色的任务就是把请求转发到子系统。类似这种结构的模式还有很多,我们先给这种类型的模式定义一个名字,叫做包装模式(wrappingpattern)。注意,包装模式是一组模式而不是一个。包装模式包括哪些设计模式呢?包装模式包括:装饰模式、适配器模式、门面模式、代理模式、桥梁模式。下面我们通过一组例子来说明这五个包装模式的区别。

    2、代理模式

    2.1 类图

      现在很多明星都有经纪人,一般有什么事他们都会说:“你找我的经纪人谈好了”,下面我们就看看这一过程怎么模拟。假设有一个追星族想找明星签字,我们看看采用代理模式怎么实现。代理模式是包装模式中的最一般的实现。类图很简单,就是一个简单的代理模式。

    2.2 代码

    2.2.1 明星接口

    class CIStar
    {
    public:
        CIStar(){}
        ~CIStar(){}
    
        //明星都会签名
        virtual void mvSign() = 0; 
    };

    2.2.2 明星

      明星只有一个行为:签字。我们来看明星的实现。

    class CSinger : public CIStar
    {
    public:
        CSinger(){}
        ~CSinger(){}
    
        void mvSign()
        {
            cout << "明星签字: 我是XXX歌手" << endl;
        }
    };

    2.2.3 经纪人

      经纪人与明星应该有相同的行为,比如说签名,虽然经纪人不签名,但是他把你要签名的笔记本、衣服、CD等传递过去让真正的明星签字。

      应该非常明确地指出一个经纪人是谁的代理,因此要在构造函数中接收一个明星对象,确定是要做这个明星的代理。

    class CAgent : public CIStar
    {
    public:
        //构造函数传递明星
        CAgent(CIStar *opStar) : mopStar(opStar) {}
        ~CAgent(){}
    
        //经纪人是不会签字的,找歌手签字
        void mvSign()
        {
            mopStar->mvSign();
        }
    
    private:
        //定义是谁的经纪人
        CIStar *mopStar;
    };

    2.2.4 场景调用

      我们再来看看追星族是怎么找明星签字的。

    int main()
    {
        //崇拜的明星是谁
        CIStar *op_star = new CSinger;
    
        //找到明星的经纪人
        CIStar *op_agent = new CAgent(op_star);
    
        cout << "追星族找歌手签名" << endl;
        op_agent->mvSign();
    
        return 0;
    }

    2.2.5 执行结果

      很简单,找到明星的代理,然后明星就签字了。运行结果如下所示:

      看看我们的程序逻辑,我们是找明星的经纪人签字,真实签字的是明星,经纪人只是把这个请求传递给明星处理而已,这是普通的代理模式的典型应用。

    3、装饰模式

    3.1 类图

      明星也都是一步一步地奋斗出来的,谁都不是一步就成为大明星的。甚至一些演员通过粉饰自己给观众一个好的印象,现在我们就来看怎么粉饰一个演员。

    3.2 代码 

      下面我们就来看看这些过程如何实现。

    3.2.1 明星接口

    class CIStar
    {
    public:
        CIStar(){}
        ~CIStar(){}
        
        //演戏
        virtual void mvAct() = 0;
    };

    3.2.2 假明星

    class CFreakStar : public CIStar
    {
    public:
        CFreakStar(){}
        ~CFreakStar(){}
    
        void mvAct()
        {
            cout << "演中: 演技很拙劣" << endl;
        }
    };

    3.2.3 抽象装饰类

      们看看这个明星是怎么粉饰的,先定义一个抽象装饰类。

    class CDecorator : public CIStar
    {
    public:
        CDecorator( CIStar *opStar ) : mopStar(opStar){}
        ~CDecorator(){}
    
        void mvAct()
        {
            mopStar->mvAct();
        }
    
    private:
        //粉饰的是谁
        CIStar *mopStar;
    };

    3.2.4 装饰类

      前后两次修饰,开演前毫无忌惮地吹嘘,吹大话装饰。

    class CHotAir : public CDecorator
    {
    public:
        CHotAir(CIStar *opStar) : CDecorator(opStar){}
        ~CHotAir(){}
    
        void mvAct()
        {
            cout << "演前: 夸夸其谈, 没有自己不能演的角色" << endl;
            CDecorator::mvAct();
        }
    };

      大家发现这个明星演技不好的时候,他拼命找借口,说是那天天气不好、心情不好等。

    class CDeny : public CDecorator
    {
    public:
        CDeny(CIStar *opStar) : CDecorator(opStar){}
        ~CDeny(){}
    
        void mvAct()
        {
            CDecorator::mvAct();
            cout << "演后: 百般抵赖, 死不承认" << endl;
        }
    };

    3.2.5 场景调用

      我们建立一个场景把这种情况展示一下。

    int main()
    {
        //定义出所谓的明星
        CIStar *op_freak = new CFreakStar;
    
        //看看他是怎么粉饰自己的
        //演前吹嘘自己无所不能
        op_freak = new CHotAir(op_freak);
        //演完后, 死不承认自己演的不好
        op_freak = new CDeny(op_freak);
    
        cout << "====看看一些虚假明星的形象====" << endl;
        op_freak->mvAct();
    
        return 0;
    }

    3.2.6 执行结果

    4、适配器模式

    4.1 类图

      我们知道在演艺圈中还存在一种情况:替身,替身也是演员,只是普通的演员而已,在一段戏中,前十五分钟是明星本人,后十五分钟也是明星本人,就中间的五分钟是替身,那这个场景该怎么描述呢?注意中间那五分钟,这个时候一个普通演员被导演认为是明星演员,我们来看类图。导演找了一个普通演员作为明星的替身,不过观众看到的还是明星的身份。

    4.2 代码

    4.2.1 明星接口

    class CIStar
    {
    public:
        CIStar(){}
        ~CIStar(){}
    
        //明星都要演戏
        virtual void mvAct(const string &sContext) = 0;
    };

    4.2.2 明星

      再来看一个具体的电影明星,他的主要职责就是演戏。

    class CFilmStar : public CIStar
    {
    public:
        CFilmStar(){}
        ~CFilmStar(){}
    
        void mvAct(const string &sContext)
        {
            cout << "明星演戏: " << sContext.c_str() << endl;
        }
    };

    4.2.3 普通演员接口

      我们再来看普通演员,明星就那么多,但是普通演员非常多,我们看其接口。

    class CIActor
    {
    public:
        CIActor(){}
        ~CIActor(){}
    
        //普通演员演戏
        virtual void mvPlayAct(const string &sContext) = 0;
    };

    4.2.4 普通演员

      普通演员也是演员,是要演戏的,我们来看一个普通演员的实现。

    class CUnknownActor : public CIActor
    {
    public:
        CUnknownActor(){}
        ~CUnknownActor(){}
    
        void mvPlayAct(const string &sContext)
        {
            cout << "普通演员: " << sContext.c_str() << endl;
        }
    };

    4.2.5 替身

    class CStandin : public CIStar
    {
    public:
        CStandin(CIActor *opActor) : mopActor(opActor){}
        ~CStandin(){}
    
        void mvAct(const string &sContext)
        {
            mopActor->mvPlayAct(sContext);
        }
    
    private:
        //替身是谁
        CIActor *mopActor;
    };

    4.2.6 场景调用

      是一个通用的替身,哪个普通演员能担任哪个明星的替身是由导演决定的,导演想让谁当就让谁当,我们来看导演。

      这里使用了适配器模式,把一个普通的演员转换为一个明星演员。

    int main()
    {
        cout << "=======演戏过程模拟==========" << endl;
        //定义一个大明星
        CIStar *op_star = new CFilmStar;
        op_star->mvAct("前十五分钟, 明星本人演戏");
        //导演把一个普通演员当做明星演员来用
        CIActor *op_actor = new CUnknownActor;
        CIStar *op_standin = new CStandin(op_actor);
        op_standin->mvAct("中间五分钟, 替身在演戏");
        op_star->mvAct("后十五分钟, 明星本人演戏");
    
        return 0;
    }

    5、桥梁模式

    5.1 类图

      我们继续说明星圈的事情,现在明星类型太多了,比如电影明星、电视明星、歌星、体育明星、网络明星等,每个类型的明星都有明确的职责,电影明星的主要工作就是演电影,电视明星的主要工作就是演电视剧或者主持电视节目。再看看现在的明星,单一发展的基本没有,主持人出专辑、体育明星演电影、歌星拍戏等太平常了,我们就用程序来表现一下多元化情形。

       类图中定义了一个抽象明星AbsStar,然后产生出各个具体类型的明星,比如电影明星FilmStar、歌星Singer,当然还可以继续扩展下去。这里还定义了一个抽象的行为AbsAction,描述明星所具有的活动,比如演电影、唱歌等,在这种设计下,明星可以扩展,明星的活动也可以扩展,非常灵活。

    5.2 代码

    5.2.1 抽象活动

      很简单,只有一个活动的描述,由子类来实现。

    class CAbsAction
    {
    public:
        CAbsAction(){}
        ~CAbsAction(){}
    
        //每个活动都有描述
        virtual void mvDesc() = 0;
    };

    5.2.2 具体活动

    // 演电影
    class CActFilm : public CAbsAction
    {
    public:
        CActFilm(){}
        ~CActFilm(){}
    
        void mvDesc()
        {
            cout << "演出精彩绝伦的电影" << endl;
        }
    };
    
    //唱歌
    class CSing : public CAbsAction
    {
    public:
        CSing(){}
        ~CSing(){}
    
        void mvDesc()
        {
            cout << "唱出优美的歌曲" << endl;
        }
    };

    5.2.3 抽象明星

      各种精彩的活动都有了,我们再来看抽象明星,它是所有明星的代表。

      明星都有自己的主要活动(或者是主要工作),我们在抽象明星中只是定义明星有活动,具体有什么活动由各个子类实现。

    class CAbsStar
    {
    public:
        //通过构造函数传递具体活动
        CAbsStar(CAbsAction *opAction) : mopAction(opAction) {}
    
        ~CAbsStar(){}
    
        virtual void mvDoJob()
        {
            mopAction->mvDesc();
        }
    
    protected:
        //一个明星参加哪些活动
        CAbsAction *mopAction;
    };

    5.2.4 电影明星

    class CFilmStar : public CAbsStar
    {
    public:
        //默认的电影明星的主要工作是拍电影
        CFilmStar() : CAbsStar(new CActFilm){}
        ~CFilmStar(){}
    
        //也可以重新设置一个新职业
        CFilmStar(CAbsAction *opAction) : CAbsStar(opAction){}
    
        //细化电影明星的职责
        void mvDoJob()
        {
            cout << "======影星的工作=====" << endl;
            CAbsStar::mvDoJob();
        }
    };

      电影明星的本职工作就应该是演电影,因此就有了一个无参构造函数来定义电影明星的默认工作,如果明星要客串一下去唱歌也可以,有参构造解决了该问题。

    5.2.5 歌星

      歌星和电影明星类似。

    class CSinger : public CAbsStar
    {
    public:
        //歌星的默认活动是唱歌
        CSinger() : CAbsStar(new CSing){}
        ~CSinger(){}
    
        //也可以重新设置一个新职业
        CSinger(CAbsAction *opAction) : CAbsStar(opAction){}
    
        //细化歌星的职责
        void mvDoJob()
        {
            cout << "======歌星的工作=====" << endl;
            CAbsStar::mvDoJob();
        }
    };

    5.2.6 场景调用

      我们使用电影明星和歌星来作为代表,这两类明星也是我们经常听到或看到的,下面建立一个场景类来模拟一下明星的事迹。

    int main()
    {
        //声明一个电影明星
        CAbsStar *op_zhang = new CFilmStar;
        //声明一个歌星
        CAbsStar *op_li = new CSinger;
        //展示一下各个明星的主要工作
        op_zhang->mvDoJob();
        op_li->mvDoJob();
        //当然, 也有部分明星不务正业, 比如歌星演戏
        cout << "不务正业的明星" << endl;
        op_li = new CSinger(new CActFilm);
        op_li->mvDoJob();
    
        return 0;
    }

    5.2.7 执行结果

      好了,各类明星都有自己的本职工作,但是偶尔客串一个其他类型的活动也是允许的,如此设计后,明星就可以不用固定在自己的本职工作上,而是向其他方向发展,比如影视歌三栖明星。

    6、门面模式

      门面模式我们在其他文章已经讲解得比较多了,本篇不再赘述。

    7、总结

      5个包装模式是大家在系统设计中经常会用到的模式,它们具有相似的特征:都是通过委托的方式对一个对象或一系列对象(例如门面模式)施行包装,有了包装,设计的系统才更加灵活、稳定,并且极具扩展性。从实现的角度来看,它们都是代理的一种具体表现形式,我们来看看它们在使用场景上有什么区别。

      代理模式主要用在不希望展示一个对象内部细节的场景中,比如一个远程服务不需要把远程连接的所有细节都暴露给外部模块,通过增加一个代理类,可以很轻松地实现被代理类的功能封装。

      装饰模式是一种特殊的代理模式,它倡导的是在不改变接口的前提下为对象增强功能,或者动态添加额外职责。就扩展性而言,它比子类更加灵活,例如在一个已经运行的项目中,可以很轻松地通过增加装饰类来扩展系统的功能。

      适配器模式的主要意图是接口转换,把一个对象的接口转换成系统希望的另外一个接口,这里的系统指的不仅仅是一个应用,也可能是某个环境,比如通过接口转换可以屏蔽外界接口,以免外界接口深入系统内部,从而提高系统的稳定性和可靠性。

      桥梁模式是在抽象层产生耦合,解决的是自行扩展的问题,它可以使两个有耦合关系的对象互不影响地扩展,比如对于使用笔画图这样的需求,可以采用桥梁模式设计成用什么笔(铅笔、毛笔)画什么图(圆形、方形)的方案,至于以后需求的变更,如增加笔的类型,增加图形等,对该设计来说是小菜一碟。

      门面模式是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道,没有它,子系统照样运行,有了它,只是更方便访问而已。

  • 相关阅读:
    codeforces C. Cows and Sequence 解题报告
    codeforces A. Point on Spiral 解题报告
    codeforces C. New Year Ratings Change 解题报告
    codeforces A. Fox and Box Accumulation 解题报告
    codeforces B. Multitasking 解题报告
    git命令使用
    shell简单使用
    知识束缚
    php 调用系统命令
    数据传输方式(前端与后台 ,后台与后台)
  • 原文地址:https://www.cnblogs.com/ChinaHook/p/7482536.html
Copyright © 2011-2022 走看看