设计模式之死磕装饰器模式(原创)

前言:

    在谈论装饰器模式之前,先谈一个笔者曾经遇到过的问题。游戏SDK(对游戏SDK开发不了解的话可以参考Android游戏SDK详解 这篇文章)分为两种,一种是渠道SDK(渠道SDK这个概念可以参考刚才的文章)还有一种叫聚合SDK。那么,什么是聚合SDK?简单点理解聚合SDK,其实就是把各个渠道同功能的接口统一归纳为一个接口,例如A公司的登录接口为ASDK.login( ) 、 B公司的登录接口为BSDK.login( ) 、C公司的登录接口为CSDK.login( )等以此类推。然后聚合SDK开发目的就是要把这些不同渠道的接口统一成自己渠道家的聚合接口。比如,自己家的聚合登录接口为MySDK.Login( ) ,那游戏方接入渠道SDK时只需要调用我们的MySDK.Login( ) 接口即可内部调用对应的渠道SDK。那么,聚合SDK的设计原则是什么?

    首先我们从现象去分析,因为渠道SDK有很多家,但是自己家的聚合SDK只能设计一套,所以聚合SDK的设计首先应该是偏向顶层设计。而且,前面也说了,聚合SDK只需要提供一套统一的API,里面去实现具体的渠道SDK,所以,它一般会被设计成抽象类或者接口。可能你对上面的论述听的云里雾里,没关系,这里只是提到我曾经遇到过的问题。那么,本篇文章介绍的装饰器模式就可以成为聚合SDK的一种设计思路。

什么是装饰器模式?

关于这一种设计模式比较官方的解释是:装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式(关于结构性模式的概念,可以参考 设计模式概念与简介 这里面详细介绍了这一概念),它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

装饰器模式应用场景:

假设我们现在按照抖音上的热门配方想去奶茶店配置一杯焦糖奶茶,这杯奶茶可以根据我们自己的喜欢添加加布丁、青稞等等。现在我们要计算调制这样一杯网红奶茶花了多少钱,那么通过代码的方式思考应该如何去做?

首先在不考虑设计模式的情况下,如果按照传统的编码思路,我们会先写一个抽象的焦糖奶茶基类、接着焦糖奶茶基类若不能满足我的需要(基本上不满足),比如要在奶茶的基础上加青稞,我们就会写一个焦糖青稞奶茶类去继承焦糖奶茶基类,返回新的价格(附加青稞的价格);比如我现在又不想要青稞(客户的需求总是很难满足呐 ~),想要加布丁,那么我们就会又写一个焦糖布丁奶茶去继承奶茶基类、后面的以此类推。虽然这种方法可以解决问题,但是这样层层继承,子类会比较膨胀,耦合性太强。

那么,我们换一种思路,既然这杯焦糖奶茶有多种可添加材料,但是它的本质依旧是一杯奶茶,添加的材料只是它附加的一种属性。按照面向对象的思想,既然附加的材料属性有很多,那么我们就可以将材料属性单独抽取成一个父类材料对象(也就是先不管奶茶,只针对附加的材料,这样做的好处是低耦合,解决子类过于膨胀的问题),让各种各样的材料分别继承材料基类即可;因此我们也就可以这样定义,焦糖奶茶就是我们的核心组件、布丁和青稞以及别的材料是我们的装饰者。

综上,我们可以首先定义一个顶层的接口,这个接口(主要是针对奶茶进行操作)定义的行为规范如:这杯奶茶花了多少钱,使用了什么材料,于是乎就有了以下代码

顶层接口

刚才也说了,附加的多种材料,我们可以单独把它抽象出来,将其设计成一个抽象类,让子类去设计定义具体的材料(使用的是青稞还是布丁)。但是,这个材料基类单独定义没有任何意义,需要将材料 放进奶茶中才能完成代码的需求(计算的是总价)。因此,这个基类需要实现最顶层的奶茶接口,然后通过构造函数,将顶层接口进行赋值操作。可能你会问,为什么要将顶层接口通过构造函数进行赋值操作,因为只有赋值操作以后,才可以再次调用顶层接口里面的方法:

材料基类

接口和基类已经简单介绍和封装完了。那么接下来我们就来定义上面提到的角色。

首先是焦糖奶茶,刚才说了,焦糖奶茶是这个功能的核心组件,而且我们也定义了最顶层的奶茶接口,所以,焦糖奶茶只需要实现顶层接口,在里面进行赋值返回操作即可,假设这杯不含任何材料的纯粹焦糖奶茶价值12毛币,那么就有以下代码:

核心组件 - 焦糖奶茶

定义完了奶茶,我们在定义客户可能想要在里面添加的青稞,布丁等等。因为在上面我们已经定义了添加材料的基类,现在只需要继承材料基类然后在子类设置附加材料的价格就可以满足计算价格以及统计材料的任务:

附加材料 - 布丁
附加材料 - 青稞 

焦糖奶茶附加材料青稞和布丁都已经准备好了,下面就等着添加测试使用了。

下面就开始测试这杯网红奶茶的价格(老板我现在要布丁以及双份的青稞)

添加多种材料的总价测试

下面是仅单独添加一种材料的结果测试:

添加一种材料的结果测试

为了加深对装饰器模式的印象,这里在举一个网上的例子:

在英雄联盟中,有个叫提莫的英雄。他是负责瓦罗兰大陆的班德尔城安全的侦察兵首领,我们知道英雄升级以后系统会赋予新的技能点供英雄去学习,那么这种英雄学技能的现象我们用装饰器模式又该如何去设计操作?

跟上面的网红奶茶一样,我们把 “提莫学技能” 分成两部分,第一部分就是我们的核心组件,也就是我们的提莫英雄;第二部分我们把提莫的技能抽取共性,成为核心组件的装饰。

首先,我们定义顶层英雄学习技能的接口:

顶层接口

接着,我们抽象技能的属性,让子类自己实现:

技能类父类

我们先实例化提莫对象:

实例化提莫

接着,轮到提莫开始学技能啦:

提莫学技能

为了方便测试,我把提莫的技能名称放在了构造函数这里,下面就是测试:

测试结果

总结:

装饰器模式是继承的一种替代模式,其优点是可以动态扩展一个实现类的功能。这种设计模式下不仅可以扩展一个类的功能,也可以动态增加功能,动态撤销。但缺点就是多层装饰使用起来相对比较复杂。本质是将具体功能职责划分(例如区分核心组件以及附加属性职责)减少子类直接继承父类的耦合性。

如果这篇文章对您有开发or学习上的些许帮助,希望各位看官留下宝贵的star,谢谢。

Ps:著作权归作者所有,转载请注明作者, 商业转载请联系作者获得授权,非商业转载请注明出处(开头或结尾请添加转载出处,添加原文url地址),文章请勿滥用,也希望大家尊重笔者的劳动成果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,408评论 4 371
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,690评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,036评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,726评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,123评论 3 296
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,037评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,178评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,964评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,703评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,863评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,333评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,658评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,374评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,195评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,988评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,167评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,970评论 2 279

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,111评论 18 139
  • 真诚的,TNANKS。 个人Github-23种设计模式案例链接 创建型模式 工厂模式 工厂模式(Factory ...
    水清_木秀阅读 25,828评论 11 204
  • 参考资料:菜鸟教程之设计模式 设计模式概述 设计模式(Design pattern)代表了最佳的实践,通常被有经验...
    Steven1997阅读 1,127评论 1 12
  • 过去 一点点堆聚成现在 欢笑 眼泪 朝阳 落晖 每一粒 都是心头 朱红的沙砾 你站立的风里...
    莫小薰阅读 337评论 0 3
  • 如果人生有起跑线的话,父母的见识、格局就是孩子的第一起跑线。 井底的鱼,不可以和他谈大海,因为他被井的狭窄束缚了;...
    豹豹儿阅读 103评论 0 0