1、概述

  装饰模式和适配器模式在通用类图上没有太多的相似点,差别比较大,但是它们的功能有相似的地方:都是包装作用,都是通过委托方式实现其功能。不同点是:装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),适配器模式则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。

  大家都应该听过丑小鸭的故事吧,我们今天就用这两种模式分别讲述丑小鸭的故事。话说鸭妈妈有5个孩子,其中4个孩子都是黄白相间的颜色,而最小的那只也就是叫做丑小鸭的那只,是纯白色的,与兄弟姐妹不相同,在遭受了诸多的嘲讽和讥笑后,最终丑小鸭变成了一只美丽的天鹅。那我们如何用两种不同模式来描述这一故事呢?

2、装饰模式描述丑小鸭

2.1 类图

  用装饰模式来描述丑小鸭,首先就要肯定丑小鸭是一只天鹅,只是因为她小或者是鸭妈妈的无知才没有被认出是天鹅,经过一段时间后,它逐步变成一个漂亮、自信、优美的白天鹅。根据分析我们可以这样设计,先设计一个丑小鸭,然后根据时间先后来进行不同的美化处理,怎么美化呢?先长出漂亮的羽毛,然后逐步展现出异于鸭子的不同行为,如飞行,最终在具备了所有的行为后,它就成为一只纯粹的白天鹅了。类图比较简单,非常标准的装饰模式。

2.2 代码

2.2.1 天鹅接口

  我们按照故事的情节发展一步一步地实现程序。初期的时候,丑小鸭表现得很另类,叫声不同,外形不同,致使周围的亲戚、朋友都对她鄙视,那我们来建立这个过程,由于丑小鸭的本质就是一个天鹅,我们就先生成一个天鹅的接口。

class CISwan
{
public:
CISwan(){};
~CISwan(){}; //天鹅会飞
virtual void mvFly() = ;
//天鹅会叫
virtual void mvCry() = ;
//天鹅都有漂亮的外表
virtual void mvDesAppaearance() = ;
};

2.2.2 丑小鸭

  我们定义了天鹅的行为,都会飞行、会叫,并且可以描述她们漂亮的外表。丑小鸭是一只白天鹅,是"is-a"的关系,也就是需要实现这个接口了。

class CUglyDuckling : public CISwan
{
public:
CUglyDuckling(){};
~CUglyDuckling(){}; //丑小鸭的叫声
void mvCry() { cout << "叫声是克噜——克噜——克噜" << endl; }; //丑小鸭的外形
void mvDesAppaearance() { cout << "外形是脏兮兮的白色, 毛茸茸的大脑袋" << endl; } //丑小鸭还比较小,不能飞
void mvFly(){ cout << "不能飞行" << endl; };
};

2.2.3 抽象装饰类

  丑小鸭具备了天鹅的所有行为和属性,因为她本来就是一只白天鹅,只是因为她太小了还不能飞行,也不能照顾自己,所以丑丑的,在经过长时间的流浪生活后,丑小鸭长大了。终于有一天,她发现自己竟然变成了一只美丽的白天鹅,有着漂亮、洁白的羽毛,而且还可以飞行,这完全是一种升华行为。我们来看看她的行为(飞行)和属性(外形)是如何加强的。

class CDecorator : public CISwan
{
public:
CDecorator(CISwan *opSwan){ mopSwan = opSwan; };
~CDecorator(){}; virtual void mvCry() { mopSwan->mvCry(); }
virtual void mvFly() { mopSwan->mvFly(); }
virtual void mvDesAppaearance() { mopSwan->mvDesAppaearance(); } private:
CISwan *mopSwan;
};

2.2.4 外形装饰

  这是一个非常简单的代理模式。我们再来看丑小鸭是如何开始变得美丽的,变化是由外及里的,有了漂亮的外表才有内心的实质变化。

class CBeautifyAppearance : public CDecorator
{
public:
CBeautifyAppearance(CISwan *opSwan) : CDecorator(opSwan) {}; // 外表美化处理
void mvDesAppaearance() override { cout << "外表是纯白色的,非常惹人喜爱!" << endl; };
};

2.2.5 行为装饰

  丑小鸭最后发现自己还能飞行,这是一个行为突破,是对原有行为“不会飞行”的一种强化。

class CStrongBehavior : public CDecorator
{
public:
CStrongBehavior(CISwan *opSwan) : CDecorator(opSwan) {};
~CStrongBehavior(){}; // 会飞行了
void mvFly() override { cout << "会飞行了!" << endl; };
};

2.2.6 场景调用

  所有的故事元素我们都具备了,就等有人来讲故事了。

int main()
{
//很久很久以前, 这里有一个丑陋的小鸭子
cout << "===很久很久以前, 这里有一只丑陋的小鸭子===" << endl; CISwan *op_duck = new CUglyDuckling;
//展示一下小鸭子
op_duck->mvDesAppaearance();
op_duck->mvCry();
op_duck->mvFly(); cout << "===小鸭子终于发现自己是一只天鹅====" << endl;
//首先外形变化了
op_duck = new CBeautifyAppearance(op_duck);
//其次行为也发生了改变
op_duck = new CStrongBehavior(op_duck);
//虽然还是叫丑小鸭, 但是已经发生了很大变化
op_duck->mvDesAppaearance();
op_duck->mvCry();
op_duck->mvFly(); return ;
}

2.2.7 执行结果

  使用装饰模式描述丑小鸭蜕变的过程是如此简单,它关注了对象功能的强化,是对原始对象的行为和属性的修正和加强,把原本被人歧视、冷落的丑小鸭通过两次强化处理最终转变为受人喜爱、羡慕的白天鹅。

3、适配器模式实现丑小鸭

3.1 类图

  采用适配器模式实现丑小鸭变成白天鹅的过程要从鸭妈妈的角度来分析,鸭妈妈有5个孩子,它认为这5个孩子都是她的后代,都是鸭类,但是实际上是有一只(也就是丑小鸭)不是真正的鸭类,她是一只小白天鹅,因为太小,差别太细微,很难分辨,导致鸭妈妈认为她是一只鸭子,从鸭子的审美观来看,丑小鸭是丑陋的。通过分析,我们要做的就是要设计两个对象:鸭和天鹅,然后鸭妈妈把一只天鹅看成了小鸭子,最终时间到来的时候丑小鸭变成了白天鹅。

  类图非常简单,我们定义了两个接口:鸭类接口和天鹅类接口,然后建立了一个适配器UglyDuckling,把一只白天鹅封装成了小鸭子。

3.2 代码

3.2.1 鸭类接口

class CIDuck
{
public:
CIDuck(){};
~CIDuck(){}; //会叫
virtual void mvCry() = ; //鸭子的外形
virtual void mvDesAppearance() = ; //描述鸭子的其他行为
virtual void mvDesBehavior() = ;
};

  鸭类有3个行为,一个是鸭会叫,一个是外形描述,还有一个是综合性的其他行为描述,例如会游泳等。

3.2.2 小鸭子

class CDuckling : public CIDuck
{
public:
CDuckling(){};
~CDuckling(){}; void mvCry() { cout << "叫声是嘎——嘎——嘎" << endl; };
void mvDesAppearance() { cout << "外形是黄白相间,嘴长" << endl; }; // 鸭子的其他行为, 如游泳
void mvDesBehavior() { cout << "会游泳" << endl; };
};

3.2.3 白天鹅

  4只正宗的小鸭子形象已经清晰地定义出来了。鸭妈妈还有一个孩子,就是另类的丑小鸭,她实际是一只白天鹅。我们先定义出白天鹅。

class CISwan
{
public:
CISwan(){};
~CISwan(){}; //天鹅会飞
virtual void mvFly() = ;
//天鹅会叫
virtual void mvCry() = ;
//天鹅都有漂亮的外表
virtual void mvDesAppaearance() = ;
}; class CWhiteSwan : public CISwan
{
public:
CWhiteSwan(){};
~CWhiteSwan(){}; //白天鹅的叫声
void mvCry() { cout << "叫声是克噜——克噜——克噜" << endl; }; //白天鹅的外形
void mvDesAppaearance() { cout << "外形是纯白色,惹人喜爱" << endl; } //天鹅是能够飞行的
void mvFly(){ cout << "能够飞行" << endl; };
};

3.2.4 当成鸭子的白天鹅

  但是,鸭妈妈却不认为自己这个另类的孩子是白天鹅,它从自己的观点出发,认为她很丑陋,有碍自己的脸面,于是驱赶她——鸭妈妈把这只小天鹅误认为一只鸭。

class CUglyDuckling : public CWhiteSwan, public CIDuck
{
public:
CUglyDuckling(){};
~CUglyDuckling(){}; void mvCry() { CWhiteSwan::mvCry(); };
void mvDesAppearance() { CWhiteSwan::mvDesAppaearance(); };
void mvDesBehavior()
{
cout << "会游泳!" << endl;
CWhiteSwan::mvFly();
}
};

3.2.5 调用

  天鹅被看成了鸭子,有点暴殄天物的感觉。我们再来创建一个场景类来描述这一场景。

int main()
{
//鸭妈妈有5个孩子, 其中4个都是一个模样
cout << "===妈妈有五个孩子, 其中四个模样是这样的: ===" << endl;
CIDuck *op_duck = new CDuckling();
op_duck->mvCry();
op_duck->mvDesAppearance();
op_duck->mvDesBehavior();
cout << "===一只独特的小鸭子, 模样是这样的: ===" << endl;
CIDuck *op_ugly_duck = new CUglyDuckling;
op_ugly_duck->mvCry();
op_ugly_duck->mvDesAppearance();
op_ugly_duck->mvDesBehavior(); return ;
}

3.2.6 执行结果

  小天鹅被认为是一只丑陋的小鸭子...采用适配器模式讲述丑小鸭的故事,我们首先观察到的是鸭与天鹅的不同点,建立了不同的接口以实现不同的物种,然后在需要的时候(根据故事情节)把一个物种伪装成另外一个物种,实现不同物种的相同处理过程,这就是适配器模式的设计意图。  

4、总结

  我们用两个模式实现了丑小鸭的美丽蜕变。我们发现:这两个模式有较多的不同点。

  ● 意图不同

  装饰模式的意图是加强对象的功能,例子中就是把一个怯弱的小天鹅强化成了一个美丽、自信的白天鹅,它不改变类的行为和属性,只是增加(当然了,减弱类的功能也是可能存在的)功能,使美丽更加美丽,强壮更加强壮,安全更加安全;而适配器模式关注的则是转化,它的主要意图是两个不同对象之间的转化,它可以把一个天鹅转化为一个小鸭子看待,也可以把一只小鸭子看成是一只天鹅,它关注转换。

  ● 施与对象不同

  装饰模式装饰的对象必须是自己的同宗,也就是相同的接口或父类,只要在具有相同的属性和行为的情况下,才能比较行为是增加还是减弱;适配器模式则必须是两个不同的对象,因为它着重于转换,只有两个不同的对象才有转换的必要,如果是相同对象还转换什么?!

  ● 场景不同

  装饰模式在任何时候都可以使用,只要是想增强类的功能,而适配器模式则是一个补救模式,一般出现在系统成熟或已经构建完毕的项目中,作为一个紧急处理手段采用。

  ● 扩展性不同

  装饰模式很容易扩展!今天不用这个修饰,好,去掉;明天想再使用,好,加上。这都没有问题。而且装饰类可以继续扩展下去;但是适配器模式就不同了,它在两个不同对象之间架起了一座沟通的桥梁,建立容易,去掉就比较困难了,需要从系统整体考虑是否能够撤销。

【设计模式】 模式PK:装饰模式VS适配器模式的更多相关文章

  1. 【设计模式】 模式PK:代理模式VS装饰模式

    1.概述 对于两个模式,首先要说的是,装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能 ...

  2. Java IO设计模式(装饰模式与适配器模式)

    01. 装饰模式 1. 定义 Decorator装饰器,就是动态地给一个对象添加一些额外的职责,动态扩展,和下面继承(静态扩展)的比较.因此,装饰器模式具有如下的特征: 它必须持有一个被装饰的对象(作 ...

  3. 【设计模式】 模式PK:包装模式群PK

    1.概述 我们讲了这么多的设计模式,大家有没有发觉在很多的模式中有些角色是不干活的?它们只是充当黔首作用,你有问题,找我,但我不处理,我让其他人处理.最典型的就是代理模式了,代理角色接收请求然后传递到 ...

  4. 设计模式GOF23(结构型模式:代理模式,适配模式,桥接模式,组合模式,装饰模式,外观模式,享元模式)

    结构型模式: – 分类: • 适配器模式.代理模式.桥接模式.装饰模式.组合模式.外观模式.享元模式 – 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题.   结构 ...

  5. 设计模式之结构类模式PK

    结构类模式包括: 适配器模式 桥梁模式 组合模式 装饰模式 门面模式 享元模式 代理模式 结构类模式着重于如何建立一个软件结构 为什么叫结构类模式呢? 因为他们都是通过组合类或对象产生更大结构以适应更 ...

  6. 设计模式:代理模式 vs 装饰模式

    参考文章:https://www.cnblogs.com/luoxn28/p/5535877.html 代理模式和装饰模式非常类似,甚至代码都类似. 二者最主要的区别是: 代理模式中,代理类对被代理的 ...

  7. 代理模式 vs 装饰模式

    代理模式和装饰模式有很大的相似性,二者的类图(几乎)是一样的.下面分别讲解代理模式和装饰模式. 1.代理模式 一般著名的跑步运动员都会有自己的代理人,如果想联系该运动员的比赛事宜,可以直接联系他的代理 ...

  8. 《Java设计模式》之装饰模式

    装饰模式(Decorator) 1.    装饰模式(Decorator)的定义:又名包装(Wrapper)模式.装饰模式以对client透明的方式扩展对象的功能,是继承关系的一个替代方案. 2.   ...

  9. 《JAVA设计模式》之装饰模式(Decorator)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述装饰(Decorator)模式的: 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替 ...

随机推荐

  1. var,let,const,三种申明变量的整理

    javascript,正在慢慢变成一个工业级语言,势力慢慢渗透ios,安卓,后台 首先let,是局部变量,块级作用域:var全局的,const是常量,也就是只读的: 一行demo说明 for (var ...

  2. JavaScript中childNodes和children的区别

    我在学习JavaScript对DOM操作的过程中,发现了使用childNodes属性,得不到我想要的结果,因此我就从JavaScript高级程序设计中了解了childNodes和children的区别 ...

  3. C语言文件基本操作

    1.用文本方式储存‘1’,‘0’,‘2’存入文件,然后用二进制方式从文件开头读出一个short型数据,并验证结果是否正确 #include<stdio.h> #include<str ...

  4. 20145214 《Java程序设计》第7周学习总结

    20145214 <Java程序设计>第7周学习总结 教材学习内容总结 时间的度量 格林威治标准时间(GMT),现已不作为标准时间使用,即使标注为GMT(格林威治时间),实际上谈到的的是U ...

  5. caffe2安装教程

    相比于网上的安装教程不如直接看官方安装教程:https://caffe2.ai/docs/getting-started.html?platform=windows&configuration ...

  6. 移动端的picker参考vux

    参考vux移动端的ui组件,做了一个picker,测试在微信,uc主流浏览器能够正常工作.而在华为浏览器根本不能使用.而测试了vux的原有picker组件,发现在华为自带浏览器中,效果依然能够实现. ...

  7. 【Docker 命令】- login 命令

    docker login : 登陆到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub docker logout : 登出一个Docker镜像仓库,如果未指定镜像 ...

  8. 【Docker 命令】- top命令

    docker top :查看容器中运行的进程信息,支持 ps 命令参数. 语法 docker top [OPTIONS] CONTAINER [ps OPTIONS] 容器运行时不一定有/bin/ba ...

  9. C#中的unsafe

    为了保持类型安全性,默认情况下,C# 不支持指针算法. 但是,通过使用 unsafe 关键字,可以定义可在其中使用指针的不安全上下文. 有关指针的详细信息,请参阅主题指针类型. 备注 在公共语言运行时 ...

  10. c#控件的name和text属性有什么不同?

    text 是显示出来,供用户和自己编辑方便使用的,name 属性是编辑代码用的. 比如要读取一个text栏的内容 取name='txtName' text='姓名'代码段需要写的是, txtName. ...