Java —— Decorator 装饰器模式
文章目录
- Java —— Decorator 装饰器模式
- 简介
- 用处
- 简单例子
- 结构
- 代码
- 涉及角色
- 相关的设计模式
- 应用实例
- 优点
- 缺点
- 使用场景
- 注意事项
- 代码
Java —— Decorator 装饰器模式
结构型模式
装饰边框与被装饰物的一致性
简介
首先看一个比喻:
假设有一块蛋糕,涂上奶油,其它什么有不加,就是奶油蛋糕。如果加上草莓,就是草莓蛋糕。如果再
加上一块黑色巧克力,上面用白色巧克力写上名,然后插上蜡烛,就变成一块生日蛋糕。
无论是蛋糕、奶油蛋糕、草莓蛋糕还是生日蛋糕,它们的核心都是蛋糕。
允许一个现有对象,添加新的功能,同时,不改变其结构。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
用处
动态地给一个对象添加一些额外的指责。就增加功能来说,装饰器模式较生成子类更为灵活。
简单例子
示例中的程序功能是给文字添加装饰边框。这里所谓的装饰边框是指用“-” 、“+” 、“|” 等字符组成的边框:
+-------------+
| Hello,world.|
+-------------+
结构
说明 | |
---|---|
Display | 用于显示字符串的抽象类 |
StringDisplay | 用于显示单行字符串的类 |
Border | 用于显示装饰边框的抽象类 |
SideBorder | 用于显示左右边框的类 |
FullBorder | 用于显示上下左右边框的类 |
Main | 测试类 |
代码
- Display
显示多行字符串的抽象类
public abstract class Display {/*** 获取横向字符数* @return*/public abstract int getColumns();/*** 获取纵向行数* @return*/public abstract int getRows();/*** 获取row行的字符串* @param row* @return*/public abstract String getRowText(int row);/*** 显示全部*/public final void show() {for(int i=0; i<getRows(); i++) {System.out.println(getRowText(i));}}}
- StringDisplay
显示单行字符串的类,肩负着Display类中声明的抽象方法
public class StringDisplay extends Display {/*** 要显示的字符串*/private String string;/*** 通过参数传入显示的字符串* @return*/public StringDisplay(String string) {this.string = string;}/*** 字节数* @return*/@Overridepublic int getColumns() {return string.getBytes().length;}/*** 行数是1* @return*/@Overridepublic int getRows() {return 1;}/*** 仅当row为0时返回值* @param row* @return*/@Overridepublic String getRowText(int row) {if(row == 0) {return string;} else {return null;}}
}
- Border
装饰边框的抽象类【通过继承,装饰边框与被装饰物具有了相同的方法】
public abstract class Border extends Display {/*** 表示被装饰物*/protected Display display;/*** 在生成实例时通过参数指定被装饰物* @param display*/protected Border(Display display) {this.display = display;}}
- SideBorder
具体的装饰边框 "|"
public class SideBorder extends Border {/*** 表示装饰边框的字符*/private char borderChar;/*** 通过构造函数指定Display和装饰边框字符* @param display* @param ch*/public SideBorder(Display display, char ch) {super(display);this.borderChar = ch;}/*** 字符数为字符串字符数加上两侧边框字符数* @return*/@Overridepublic int getColumns() {return 1 + display.getColumns() + 1;}/*** 行数即被装饰物的行数* @return*/@Overridepublic int getRows() {return display.getRows();}/*** 指定的那一行的字符串为被装饰物的字符串加上两侧的边框的字符* @param row* @return*/@Overridepublic String getRowText(int row) {return borderChar + display.getRowText(row) + borderChar;}
}
- FullBorder
在字符串上下左右都加上装饰边框
public class FullBorder extends Border {public FullBorder(Display display) {super(display);}/*** 字符数为被装饰物的字符数加上两侧边框字符数* @return*/@Overridepublic int getColumns() {return 1 + display.getColumns() + 1;}/*** 行数为被装饰物的行数加上上下边框的行数* @return*/@Overridepublic int getRows() {return 1 + display.getRows() + 1;}/*** 指定的那一行的字符串* @param row* @return*/@Overridepublic String getRowText(int row) {if(row == 0) {// 下边框return "+" + makeLine('-', display.getColumns()) + "+";} elseif(row == display.getRows() + 1) {// 上边框return "+" + makeLine('-', display.getColumns()) + "+";} else {// 其它边框return "|" + display.getRowText(row - 1) + "|";}}/*** 生成一个重复count次字符ch的字符串* @param ch* @param count* @return*/private String makeLine(char ch, int count) {StringBuffer buf = new StringBuffer();for(int i=0; i<count; i++) {buf.append(ch);}return buf.toString();}}
- Main
public class Main {public static void main(String[] args) {// 显示单行字符串的类(相当于蛋糕的核心蛋糕[蛋糕胚子])Display b1 = new StringDisplay("Hello, world.");// 具体的装饰边框 "|"Display b2 = new SideBorder(b1, '#');// 在字符串上下左右都加上装饰边框Display b3 = new FullBorder(b2);b1.show();b2.show();b3.show();Display b4 = new SideBorder(new FullBorder(new FullBorder(new SideBorder(new FullBorder(new StringDisplay("你好, 世界。")),'*'))),'/');b4.show();}
}
- 运行结果
Hello, world.
#Hello, world.#
+---------------+
|#Hello, world.#|
+---------------+
/+-----------------+/
/|+---------------+|/
/||*+-----------+*||/
/||*|你好, 世界。|*||/
/||*+-----------+*||/
/|+---------------+|/
/+-----------------+/
- Main时序图
涉及角色
- Component
增加功能时核心角色。
以上述比喻来说,装饰前的蛋糕就是Component角色。Component角色只是定义蛋糕的接口(API)。
示例中,由Display代表此角色。 - ConcreteComponent
实现Component角色定义的具体蛋糕。
示例中,StringDisplay代表此角色。 - Decorator(装饰物)
具有与Component角色相同的接口(API)。在它内部保存了被装饰对象 —— Component角色。Decorator知道自己要装饰的对象。
示例中,Border代表此角色。 - ConcreteDecorator(具体的装饰物)
具体的Decorator角色。
示例中,SideBorder和FullBorder代表此角色。
相关的设计模式
- Adapter 适配器模式
Decorator装饰器模式可以在不改变被装饰物的接口(API)的前提下,为被装饰物添加边框(透明性)。
Adapter适配器模式用于适配两个不同的接口(API)。 - Strategy 策略模式
Decorator装饰器模式可以像改变被装饰物的边框或是为被装饰物添加多重边框那样,来增加类的功能。
Strategy 策略模式通过整体地替换算法来改变类的功能。
应用实例
- 孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。
- 不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点
多层装饰比较复杂。
使用场景
- 扩展一个类的功能。
- 动态增加功能,动态撤销。
注意事项
可代替继承。
代码
GitHub —— Decorator 装饰器模式
Java —— Decorator 装饰器模式相关推荐
- JAVA设计模式-装饰器模式(Decorator)
装饰器模式(Decorator) 为了某个实现类在不修改原始类的基础上进行动态地覆盖或者增加方法 采用--------装饰器模式 实现类要保持与原有类的层级关系 装饰器模式是一种特殊的适配器模式 拿适 ...
- java设计模式----装饰器模式
Decorator装饰器,顾名思义,就是动态地给一个对象添加一些额外的职责,就好比为房子进行装修一样.因此,装饰器模式具有如下的特征: 它必须具有一个装饰的对象. 它必须拥有与被装饰对象相同的接口. ...
- Java设计模式-装饰器模式 理论代码相结合
继Java设计模式适配器模式后的装饰器模式来啦,让我们一起看看吧. 会了就当复习丫,不会来一起来看看吧. 很喜欢一句话:"八小时内谋生活,八小时外谋发展". 如果你也喜欢,让我们一 ...
- Java设计模式----------装饰器模式
1.介绍 装饰器模式是一种结构型的设计模式.使用该模式的目的是为了较为灵活的对类进行扩展,而且不影响原来类的结构.有同学说可以通过继承的方式进行实现啊,没错,继承的确可以实现,但是继承的成本相对比较高 ...
- java设计模式--装饰器模式
转载 http://sishuok.com/forum/blogPost/list/5766.html 22.1 场景问题 22.1.1 复杂的奖金计算 考虑这样一个实际应用:就是如何实现灵活的奖金计 ...
- java设计模式---装饰器模式
装饰者模式 动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 具体被装饰者和抽象装饰类都继承于抽象被装饰者类,继承的是类型,而不是行为.行为来自装饰者和基础组件,或与其他 ...
- Decorator 装饰器模式 -动态组合
为什么80%的码农都做不了架构师?>>> 一:业务场景 奖金计算,每个部门,有不同的计算方法,且每个部门有不同类型的奖金项:而且每年或每隔几个季度奖金算法都要重新实现下. 这个 ...
- 6中结构型设计模式的对比理解(Composite组合模式,Proxy代理模式,Flyweight享元模式,Facade门面模式,Bridge桥接模式,Decorator装饰器模式)
结构型模式 结构型模式用来组装 类和对象,以获得更大的结构. 结构型类模式,通过继承机制来组合接口或类.简单的例子就是多重继承,最后一个类拥有所有父类的性质.这个模式有助于独立开发一个协同类.另一个例 ...
- decorator java_装饰器模式-Decorator(Java实现)
装饰器模式-Decorator(Java实现) 装饰器模式允许向一个现有的对象添加新的功能, 同时又不改变其结构. 其中 "现有对象"在本文中是StringDisplay类. 添加 ...
最新文章
- Andrej Karpathy发文谈神经网络后,引发的对硬件,软件和学件的思考
- TCP 三次握手四次挥手
- linux命令行安装谷歌浏览器,Linux(ubuntu) 三行代码搞定安装谷歌浏览器
- 分布式算法(一致性Hash算法)
- 使用FFmpeg类库实现YUV视频序列编码为视频
- java网络爬虫基础学习(二)
- 团队开发——冲刺1.e
- 读取位置时发生访问冲突 c++_王道计算机网络|第三章数据链路层1314介质访问控制...
- c++ 中引用()的用法和应用实例
- SRMD:Learning a Single Convolutional Super-Resolution Network for Multiple Degradations
- 【参赛作品19】【openGauss】gsql客户端工具(二)gsql客户端工具之Data Studio客户端工具
- 如何将PDF转换成jpg图片?教你2种免费方法
- 看不看?这就是程序员996的真实内幕!
- 《基于 DirectX11 的 3D 图形程序设计案例教程》学习一 HelloWorld
- 计算机操作员考试模拟在线考试,计算机操作员高级问答集考试卷模拟考_试题...
- 这和计算机列表浏览服务器有关,电脑升级win10后,必做的40项性能优化
- 微信小程序 音乐播放控件,监听播放事件, 音乐播放的基本实现
- 电子学会2022年9月青少年软件编程(图形化)等级考试试卷(二级)答案解析
- HHVM安装(centos6.3下)
- VUE前段开发-开发环境搭建和开发工具安装
热门文章
- 数据分析与挖掘实战-应用系统负载分析与磁盘容量预测
- Error response from daemon: Container is restarting,wait until the container is running
- 从0到1,我们一起调试温控仪表
- 网站域名是不是ftp服务器地址,什么是FTP地址?
- [SIM] MT6589 W+G/G+G dual-talk找不到SIM卡2
- 四种方法 恢复损坏的Excel文档
- 优酷在计算机上无法联网,为什么优酷电脑版看不了
- 心理学中的一些故事引出的各种效应
- Linux iostate命令实战
- [转]绿盟非常务实的拒信