设计模式之装饰器模式(decorator pattern)
装饰器模式主要对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。是一种对象结构型模式。需要注意的是,该过程是通过调用被包裹之后的对象完成功能添加的,而不是直接修改现有对象的行为,相当于增加了中间层。类似于python中的@装饰器。
下面还是按照老规矩,先来了解一下该模式相关的概念和原理,然后通过两个具体的实例体会一下如何在实际开发中应用该模式。
1. 目的
可以动态的为同一类的不同对象加以修饰以添加新的功能。
2. 动机
灵活的对类对象功能进行扩展。
3. 优缺点
优点:
- 相比较于类的继承来扩展功能,对对象进行包裹更加的灵活;
- 装饰类和被装饰类相互独立,耦合度较低;
缺点:
- 没有继承结构清晰;
- 包裹层数较多时,难以理解和管理;
4. 应用场景
- 动态的增加对象的功能;
- 不能以派生子类的方式来扩展功能;
- 限制对象的执行条件;
- 参数控制和检查等;
5. 原理
下面是GoF介绍的典型的装饰器模式的UML类图:

Component:
对象的接口类,定义装饰对象和被装饰对象的共同接口;
ConcreteComponent:
被装饰对象的类定义;
Decorator:
装饰对象的抽象类,持有一个具体的被修饰对象,并实现接口类继承的公共接口;
ConcreteDecorator:
具体的装饰器,负责往被装饰对象添加额外的功能;
说明:
由于这个模式从实际的例子来理解更加的直观方便,因此这里不再单独的实现上面的UML结构代码。
6.实例——画图
先来通过一个简单的画图的实例来直观感受一下。
前提:
系统中存在一个画圆的类,该类只是用来画圆,以及其他一些大小和位置等参数的控制。
新加需求:
- 可以对圆的边进行着色
- 可以对圆填充颜色;
- 可以同时对边和内部着色;
这个需求的常规方法实现可能如下:
- 对画圆类进行迭代,以支持边和内部颜色填充 ;
- 画圆类作为父类,分别定义三个子类,继承父类的画圆方法,子类分别实现对应的作色需求;
上面的两个方法都是可行的,也是比较直观的,这里我们尝试使用装饰器模式来实现,作为以上两种方法的对比。
下面来看一下装饰器模式实现该需求的UML类图:

接口类:shape
public interface Shape {
void draw();
}
画圆类:Circle
public class Circle implements Shape {
@Override
public void draw() {
System.out.print("a circle!");
}
}
抽象装饰器类:Decorator
public abstract class Decorator implements Shape {
protected Shape circle;
public Decorator(Shape shape) {
circle = shape;
}
public void draw() {
circle.draw();
}
}
为圆边着色装饰器类:CircleEdge
public class CircleEdge extends Decorator {
public CircleEdge(Shape circle) {
super(circle);
}
private void setEdgeColor() {
System.out.print(", edge with color");
}
public void draw() {
circle.draw();
setEdgeColor();
}
}
为圆填充颜色装饰器类:CircleEdge
public class CircleFill extends Decorator {
public CircleFill(Shape circle) {
super(circle);
}
private void setEdgeFill() {
System.out.print(", content with color");
}
public void draw() {
circle.draw();
setEdgeFill();
}
}
演示:
public class Demo {
public static void main(String[] args) {
Shape circle = new Circle();
circle.draw();
System.out.println("");
Decorator circleEdge = new CircleEdge(circle);
circleEdge.draw();
System.out.println("");
Decorator circleFill = new CircleFill(circle);
circleFill.draw();
System.out.println("");
Decorator circleEdgeFill = new CircleFill(circleEdge);
circleEdgeFill.draw();
}
}
结果:
a circle!
a circle!, edge with color
a circle!, content with color
a circle!, edge with color, content with color
上面我们通过实现两个装饰器分别完成对边着色和填充的需求,通过对装饰器的进一步装饰,我们完成了同时着色的需求。
7.实例——网络数据报封装
接下来我们在使用网络数据传输的例子来体会一下装饰器模式,下图表示的是应用层的文件传输协议FTP通过TCP来传输数据:

虽然应用层可以越过传输层直接使用网络层进行数据发送(如,ICMP),但多数都会使用传输层的TCP或者UDP进行数据传输的。
下面我们用装饰器模式来表示一下应用层数据通过传输层来发送数据,UML类图如下:

上述图中表示了,应用层的数据通过添加TCP头或者UDP头,然后通过下面的网络层send数据。
数据报接口类:Datagram
public interface Datagram {
void send(); // 通过网络层发送IP数据报
}
应用层数据类:AppDatagram
public class AppDatagram implements Datagram {
@Override
public void send() {
System.out.print("send IP datagram!");
}
}
传输层类(抽象装饰器):TransportLayer
public abstract class TransportLayer implements Datagram {
protected Datagram appData;
public TransportLayer(Datagram appData) {
this.appData = appData;
}
public void send() {
appData.send();
}
}
添加TCP头部类:UseTCP
public class UseTCP extends TransportLayer {
public UseTCP(Datagram appData) {
super(appData);
}
private void addHeader() {
System.out.print("Appdata add TCP header, ");
}
public void send() {
addHeader();
appData.send();
}
}
添加TCP头部类:UseUDP
public class UseUDP extends TransportLayer {
public UseUDP(Datagram appData) {
super(appData);
}
private void addHeader() {
System.out.print("Appdata add UDP header, ");
}
public void send() {
addHeader();
appData.send();
}
}
演示:
public class Demo {
public static void main(String[] args) {
Datagram appData = new AppDatagram();
appData.send();
System.out.println("");
TransportLayer tcpData = new UseTCP(appData);
tcpData.send();
System.out.println("");
TransportLayer udpData = new UseUDP(appData);
udpData.send();
System.out.println("");
}
}
结果:
send IP datagram!
Appdata add TCP header, send IP datagram!
Appdata add UDP header, send IP datagram!
当然这里例子中已经添加过TCP头部的数据报不能再使用UDP传输了,无意义,也被必要。
8. 总结
其实所谓装饰器,本质上是对现有类对象的包裹,得到一个加强版的对象。
和python中@装饰器不同的是:
- python中的装饰器是作用于函数或者类定义的,并直接覆盖掉了原来函数或者类的定义;
- 装饰器模式仅仅是修改了了已经产生的对象的行为,和类定义没有半点关系;
通过上面的两个例子,应该对装饰器模式有了一个简单的认识。
另外,要体会到什么时候用继承什么时候用装饰器。
参考:
GoF《Design Patterns: Elements of Reusable Object-Oriented Software》
https://www.runoob.com/design-pattern/decorator-pattern.html
设计模式之装饰器模式(decorator pattern)的更多相关文章
- python 设计模式之装饰器模式 Decorator Pattern
#写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...
- 23种设计模式之装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...
- c#设计模式之装饰器模式(Decorator Pattern)
引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...
- 【UE4 设计模式】装饰器模式 Decorator Pattern
概述 描述 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活.是一种对象结构型模式. 套路 抽象构件(Component) 具体构 ...
- 设计模式学习--装饰者模式(Decorator Pattern)
概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...
- 设计模式(三)——装饰器模式(Decorator Pattern)
发现太过于刻意按照计划来写博客,有点不实际,刚好最近在一个网课上复习AOP的知识,讲到了装饰器模式和代理模式,顺便复习总结一下. 首先了解一下装饰器模式,从名字里面可以看出来,装饰器模式就类似于房子装 ...
- 用最简单的例子理解装饰器模式(Decorator Pattern)
假设有一个公司要做产品套餐,即把不同的产品组合在一起,不同的组合对应不同的价格.最终呈现出来的效果是:把产品组合的所有元素呈现出来,并显示该组合的价格. 每个产品都有名称和价格,首先设计一个关于产品的 ...
- C#设计模式之装饰者模式(Decorator Pattern)
1.概述 装饰者模式,英文名叫做Decorator Pattern.装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 2 ...
- php装饰器模式(decorator pattern)
十一点了. <?php /* The decorator pattern allows behavior to be added to an individual object instance ...
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
随机推荐
- node 设置自动启用定时任务控件 node-schedule
[转]Quartz中时间表达式的设置-----corn表达式 时间格式: <!-- s m h d m w(?) y(?) -->, 分别对应: 秒>分>小时>日&g ...
- Qt、Qte与Qtopia(Qt嵌入式的发展历程)
Qt的授权是分为两条线,商业版和开源版.如果使用商业版的Qt,那么开发出的程序可以是私有的和商业的:如果使用的是开源版的Qt,由于其使用的是GPL协议,那么可发出的程序也必须是GPL的.不过自从qt ...
- Windows安装Linux子系统--安装GUI界面
原文:Windows安装Linux子系统--安装GUI界面 前段时间发现Windows可以安装Linux子系统了,恰逢电脑换了固态,还没装Linux,不如趁机体验一番! 1.准备工作 1.1.打开 ...
- ASP.NET Core Windows 环境配置 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core Windows 环境配置 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Windows 环境配置 ASP.NET Core ...
- 楼塔当天领袖acm心理(作为励志使用)
楼主个人博客:吉尔博客 假期空闲的时候使用.这些年来GCJ.ACM,TopCoder 的一个号码的一重要的比赛的参与 回顾.GCJ2006 的回顾,今天时间上更早一些吧,我如今还清晰记得3 年 前.我 ...
- C++ 中间statickeyword
static顾名思义是一个静态的含义.在此我想谈谈相关的系统statickeyword角色,当然,这主要是对语言的发展C与C++角色,在信息方面的作用等方面,请另找.在评论中肯定有不恰当,请大胆地抛砖 ...
- 使用StringBuilder与SqlParameter
好处: 防止sql注入:占用内存更少 例子: 传参有业务查询条件startDate,endDate,A,每页数据个数pageSize,当前查询页码pageIndex string sql = @&qu ...
- WPF里DataGrid分页控件
1.主要代码: using System; using System.Collections.ObjectModel; using System.Windows; using System.Windo ...
- 你遗忘的都在这里—iOS常用类型方法笔记
这些都是项目中常用但又常忘的方法,与大家分享一下. 一.NSString 创建字符串. NSString *astring = @"This is a String!"; 创建空 ...
- wpf VisualBrush 的使用,可创建重复图像
VisualBrush 类(msdn) <Grid.Background> <VisualBrush TileMode="Tile" Viewport=" ...