设计模式:装饰模式

这是张富涛的第4篇原创

设计模式:装饰模式

1. 提问

这次的设计模式分享与以往不同,以往我们先介绍设计模式,今天我先抛出一个问题吧:

“假如你进入了一家生产智能硬件的公司,现在要你来以面向对象的设计方式设计他的智能硬件类。以下是一些条件:

  • 已知他们生产了多种智能硬件,如手环、手机等。

  • 每种硬件的配件又有很多,如普通的手机、带彩屏的手机、带蓝牙的手机、带摄像头的手机等等。

对于Model类你会怎么设计?”

你的同事小明提出了如下设计:

他解释:以面向对象的思路来说,所有“手环”继承于Handware(硬件)类,在这之下“彩屏手环”继承“手环”,“蓝牙手环”也继承于“手环”。

这种思路我们仔细思索一下:这种设计有明显的不足:

  1. 配件”的种类较多,造成最终的类数量会相当庞大,无法维护。

  2. 我们无法预知类似某个手机装了双摄像头的情况。

  3. 我们多个“配件”无法组合使用,例如“彩屏蓝牙手机”、“彩屏蓝牙摄像手机”等。

这种情况我们正是我们使用“装饰模式”的时机!

2. 概述

装饰模式(Decorator Pattern):有时也叫“装饰者模式”或“包装模式”。在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,。

3. 主要解决问题(何时使用)

  1. 需要扩展一个类的功能,或给一个类增加附加责任。

  2. 需要动态的给一个对象增加功能,这些功能可以再动态地撤销。

  3. 需要增加一些基本功能的排列组合而产生的非常大量的功能,从而使继承变得不现实。

4. 设计实现

那么下面我们看一下“装饰模式”的设计实现

将“Handware”类定义为一个接口,规定了公共的方法,此处设计为“show()”(展示、使用)方法。“手环”、“手机”等需要被装饰的硬件实现“Handware”接口,并实现方法,方法内展示了最基本的功能:比如“手环”最基本的方式是“计步”,手机最基本的功能是“拨打电话”、“收发短信”。

同时我们设定了一个“Decorator”类,这个类的功能是:规范“具体装饰类”。

最后我们创建了一些“Decorator”类的子类,他们是具体装饰者,用来装饰“手环”、“手机”等类。他们继承了“Decorator”类,同时重写了方法。

5. 装饰模式的角色组成

以下角色请联系我们的示例理解:

  1. 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。(类似“Handware”硬件类。)

  2. 具体构件(ConcreteComponent)角色:被装饰角色。(类似手环、手机类)

  3. 装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。(图中“Decorator”类)

  4. 具体装饰角色(ConcreteDecorator):负责给构件对象“贴上”附加的责任。(类似彩屏、蓝牙、摄像类)

说了这么多,下面就让我们真刀真枪的看看代码的具体实现吧!

5. 代码实现

抽象构件角色:Handware(智能硬件):

具体构件角色:Phone(手机)

具体构件角色:Shouhuan(手环)

为了方便理解,以下类名不采用专业英文,都用拼音命名

装饰角色:Decorator

具体装饰角色:Caiping(彩屏)

具体装饰角色:Lanya(蓝牙)

具体装饰角色:SheXiangtou(摄像头)

测试类

最终输出结果:

如果采用同事“小明”的设计,有4个硬件,5个配件的话,我们至少要创建出20个类,而且无法随意搭配,如果多个配件进行搭配的话,我们要单独的设计出类。但是经过这样的设计,我们只要9个类就可以了,并且可以随意将配件进行搭配,这不但优化了继承,并且更加灵活,可以为一个对象动态的增加、拆卸职责、功能(类似动态增加一个配件或拆卸一个配件,增加一个功能,拆卸一个功能)。

6. 透明性要求

装饰模式对客户端的透明性要求程序不要声明一个“具体装饰角色”类型的变量(彩屏、蓝牙、摄像头),而应当声明一个“抽象构建角色”类型的变量(智能硬件)。

以下的写法是正确的:

以下的写法是不正确的:

无论最初new出的“Handware”类是如何进行“装饰”的,他最终依然是“Handware”类,而不是“Caiping”、"Lanya"或者“SheXiangtou”类,客户端一直拿到的是“Handware”类,从面向对象的角度来说,“手机”无论怎么样都是“智能硬件”,“彩屏手机”、“蓝牙手机”都是“智能硬件”,这些都是“装饰”了最初的“智能硬件”。

可是我们注意到,对于开篇说的“装饰模式”针对性解决的问题,我们目前只解决了2、3条,可是第1条并没有解决,让我们回顾一下“装饰模式”针对性解决的问题:

  1. 需要扩展一个类的功能,或给一个类增加附加责任。

  2. 需要动态的给一个对象增加功能,这些功能可以再动态地撤销。

  3. 需要增加一些基本功能的排列组合而产生的非常大量的功能,从而使继承变得不现实。

这就要涉及到一个概念:“装饰模式的半透明模式

7. 半透明模式

理想的装饰模式在对被装饰对象进行功能增强的同时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。可是现实中我们很难找到这种纯粹的装饰模式,大多数的装饰模式的实现都是“半透明”的,而不是完全透明的。换言之,允许装饰模式改变接口,增加新的方法。

纯粹的装饰模式很难找到。装饰模式的用意是在不改变接口的前提下,增强所考虑的类的性能。在增强性能的时候,往往需要建立新的公开的方法。即便是在孙大圣的系统(如下图)里,也需要新的方法。比如齐天大圣类并没有飞行的能力,而鸟儿有。这就意味着鸟儿应当有一个新的fly()方法。再比如,齐天大圣类并没有游泳的能力,而鱼儿有,这就意味着在鱼儿类里应当有一个新的swim()方法。

这就导致了大多数的装饰模式的实现都是“半透明”的,而不是完全透明的。换言之,允许装饰模式改变接口,增加新的方法。这意味着客户端可以声明“具体装饰角色”类型的变量(彩屏、蓝牙、摄像头),从而可以调用“具体装饰角色”类中才有的方法:

8. 结尾

以上就是“装饰模式”啦,看到这里,我们可能还不清楚具体应用到什么地方,拿我们公司的一种业务举例:我们“万方数据知识服务平台”中有为机构开设的账号,而机构在我们可以购买一种“在限定时间内可以无限使用”的限时业务,或者是“这种业务使用多少次”,我们都为其开设一种账号,如“普通机构账号”、“限时资源账号”、“次数机构账号”,这个时候我们的设计是不是就可以使用“装饰模式”了呢?

那么分享就到这里啦,不出意外的话这大概是“设计模式篇”里最后分享的一种设计模式,虽然设计模式总共有23种,可是除了“单例模式”、“工厂模式”之外最常用的几种都已经分享了,而且我们平时不知不觉使用了一些设计模式。如果我们能在项目中用到1~2个设计模式就已经非常厉害了!如果有些小伙伴希望我分享其他的设计模式也可以给我留言说出您希望分享的设计模式哦!



---------------

公众号:张富涛的学习笔记(ID:futaoNT)

知乎:张富涛

CSDN:张富涛

B站:你给的宝物

这是一个在夜晚可以靠编程拯救世界的程序员,关注他将在第一时间获悉他的知识、工作心得!

长按下图二维码关注:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值