如何利用装饰者模式在不改变原有对象的基础上扩展功能
点击上方蓝色“ 方志朋 ”,选择“设为星标”
回复“666”获取独家整理的学习资料!
作者:双子孤狼
blog.csdn.net/zwx900102/article/details/107740212
阅读目录
-
什么是装饰者模式
-
普通示例
-
装饰者模式示例
-
类图关系
-
装饰者模式使用场景
-
装饰者模式优点
-
装饰者模式缺点
什么是装饰者模式
装饰者模式(DecoratorPattern)是指在不改变原有对象的基础之上,将功能附加到对
象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。
装饰者模式在生活中也有很多形象的例子,比如说给蛋糕加上一些水果,给披萨加上榴莲,或者说给烧饼加上鸡蛋火腿之类等等。
下面我们就以给蛋糕加上水果为例来看看如果不用装饰者模式要怎么实现,如果使用装饰者模式又要怎么实现,对比之后就知道装饰者模式的优势了。
普通示例
新建一个普通的蛋糕类:
package com.zwx.design.pattern.decorator.common; import java.math.BigDecimal; public class Cake { public String getCakeMsg(){ return "我是一个8英寸的普通蛋糕"; } public BigDecimal getPrice(){ return new BigDecimal("68"); } }
这时候,我们需要给蛋糕加点芒果,那可以再新建一个类去继承普通Cake类,然后重写其中的方法:
package com.zwx.design.pattern.decorator.common; import java.math.BigDecimal; public class CakeAddMango extends Cake { @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个芒果"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("10")); } }
这时候,如果不仅仅加芒果,还要再加点葡萄,那么可以再写一个类,继承CakeAddMango,然后重写其中的方法。
package com.zwx.design.pattern.decorator.common; import java.math.BigDecimal; public class CakeAddMangoAndGrape extends CakeAddMango { @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个葡萄"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("5")); } }
写个测试类测一下:
package com.zwx.design.pattern.decorator.common; public class TestCake { public static void main(String[] args) { //普通蛋糕 Cake cake = new Cake(); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //加芒果蛋糕 CakeAddMango cakeAddMango = new CakeAddMango(); System.out.println(cakeAddMango.getCakeMsg() + ",价格:" + cakeAddMango.getPrice()); //加芒果和葡萄蛋糕 CakeAddMangoAndGrape cakeAddMangoAndGrape = new CakeAddMangoAndGrape(); System.out.println(cakeAddMangoAndGrape.getCakeMsg() + ",价格:" + cakeAddMangoAndGrape.getPrice()); } }
输出如下结果:
我是一个8英寸的普通蛋糕,价格:68 我是一个8英寸的普通蛋糕+1个芒果,价格:78 我是一个8英寸的普通蛋糕+1个芒果+1个葡萄,价格:83
看起来挺好的,能实现,但是假如我们加2个芒果呢?或者是我要加2个普通呢,或者说芒果和葡萄要组合,数量不一定,那利用现有的类是无法实现的,只能不断加类去重写,如果业务变更频繁,修改起来会是致命的。
正因为普通的实现方法有这种缺陷,才有了装饰者模式,接下来我们来看看同一个需求利用装饰者模式是怎么实现的吧。
往期面试题:001期~180期汇总
装饰者模式示例
1、新建一个蛋糕的抽象类:
package com.zwx.design.pattern.decorator; import java.math.BigDecimal; public abstract class Cake { public abstract String getCakeMsg(); public abstract BigDecimal getPrice(); }
2、然后新建一个普通蛋糕的类:
package com.zwx.design.pattern.decorator; import java.math.BigDecimal; public class BaseCake extends Cake { @Override public String getCakeMsg() { return "我是一个8英寸的普通蛋糕"; } @Override public BigDecimal getPrice() { return new BigDecimal("68"); } }
3、新建一个蛋糕的装饰器类,内部持有蛋糕Cake对象,这个就是扩展的关键:
package com.zwx.design.pattern.decorator; import java.math.BigDecimal; public abstract class CakeDecorator extends Cake{ private Cake cake; public CakeDecorator(Cake cake) { this.cake = cake; } @Override public String getCakeMsg() { return this.cake.getCakeMsg(); } @Override public BigDecimal getPrice() { return this.cake.getPrice(); } }
4、新建一个芒果蛋糕的装饰器类继承CakeDecorator类:
package com.zwx.design.pattern.decorator; import java.math.BigDecimal; public class CakeAddGrapeDecorator extends CakeDecorator { public CakeAddGrapeDecorator(Cake cake) { super(cake); } @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个葡萄"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("5")); } }
5、新建一个葡萄的装饰器类继承CakeDecorator类:
package com.zwx.design.pattern.decorator; import java.math.BigDecimal; public class CakeAddMangoDecorator extends CakeDecorator { public CakeAddMangoDecorator(Cake cake) { super(cake); } @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个芒果"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("10")); } }
6、最后写一个测试类测试一下:
package com.zwx.design.pattern.decorator; public class TestCakeDecorator { public static void main(String[] args) { Cake cake = null; //普通蛋糕 cake = new BaseCake(); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //加一个芒果 cake = new CakeAddMangoDecorator(cake); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //加一个葡萄 cake = new CakeAddGrapeDecorator(cake); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //再加一个芒果 cake = new CakeAddMangoDecorator(cake); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); } }
输出结果为:
我是一个8英寸的普通蛋糕,价格:68 我是一个8英寸的普通蛋糕+1个芒果,价格:78 我是一个8英寸的普通蛋糕+1个芒果+1个葡萄,价格:83 我是一个8英寸的普通蛋糕+1个芒果+1个葡萄+1个芒果,价格:93
我们可以看到,使用装饰者模式之后,扩展之前的功能变得极为方便,可以根据现有的装饰器进行任意组合。
类图关系
看一下类图,首先是一个基础抽象类定义了基本方法,然后是基础实现和基础装饰器继承并重写抽象类中的方法:
装饰者模式使用场景
-
1、用于扩展一个类的功能或给一个类添加附加职责。
-
2、动态的给一个对象添加功能,这些功能可以再动态的撤销。
注:MyBatis中的二级缓存就是用了装饰者模式来进行动态扩展,感兴趣的可以去了解下。
往期面试题:001期~180期汇总
装饰者模式优点
-
1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。
-
2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
-
3、装饰者完全遵守开闭原则。
装饰者模式缺点
-
1、会出现更多的代码,更多的类,增加程序复杂性。
-
2、动态装饰以及多层装饰时会更加复杂。
热门内容:
最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡
本文分享自微信公众号 - 方志朋(walkingstory)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java实现单链表、栈、队列三种数据结构
点击上方蓝色“ 方志朋 ”,选择“设为星标” 回复“666”获取独家整理的学习资料! 作者:远航 cnblogs.com/yang-guang-zhang/p/13884023.html 一、单链表 1、在我们数据结构中,单链表非常重要。它里面的数据元素是以结点为单位,每个结点是由数据元素的数据和下一个结点的地址组成,在java集合框架里面 LinkedList、HashMap(数组加链表)等等的底层都是用链表实现的。 2、下面是单链表的几个特点: 数据元素在内存中存放的地址是不连续的:单链表的结点里面还定义一个结点,它里面保存着下一个结点的内存地址,在实例化对象的时候,jvm会开辟不同内存空间,并且是不连续的。 添加效率高:添加一个元素时,先找到插入位置的前一个,只需要将1,2个元素的连接断开,将插入结点的next指向第一个元素的next (1),然后将第一个元素的next指向插入结点(2), 不用在挪动后面元素。 删除效率高:删除一个元素时,先找到删除位置,将前一个的next指向删除位置的next,不用在挪动后面元素。 查询效率低:查询的时候必须从头开始,依次遍历,而数组因为它的内...
- 下一篇
什么是链路追踪?分布式系统如何实现链路追踪?
公众号关注 “ 杰哥的IT之旅 ”, 选择“ 星标 ”, 重磅干货,第一 时间送达! 在分布式系统,尤其是微服务系统中,一次外部请求往往需要内部多个模块,多个中间件,多台机器的相互调用才能完成。在这一系列的调用中,可能有些是串行的,而有些是并行的。在这种情况下,我们如何才能确定这整个请求调用了哪些应用?哪些模块?哪些节点?以及它们的先后顺序和各部分的性能如何呢? 这就是涉及到链路追踪。 什么是链路追踪? 链路追踪是分布式系统下的一个概念,它的目的就是要解决上面所提出的问题,也就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如,各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。 链路追踪的原理 衡量一个接口,我们一般会看三个指标: 1、接口的 RT(Route-Target)你怎么知道? 2、接口是否有异常响应? 3、接口请求慢在哪里? 1、单体架构时代 在创业初期,我们的系统一般是单体架构,如下: 对于单体架构,我们可以使用 AOP(切面编程)来统计这三个指标,如下: 使用 AOP(切面编程),对原本的逻辑代码侵入更少,我们只需要在调用...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7设置SWAP分区,小内存服务器的救世主
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS关闭SELinux安全模块
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker安装Oracle12C,快速搭建Oracle学习环境