装饰器模式主要对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。是一种对象结构型模式。需要注意的是,该过程是通过调用被包裹之后的对象完成功能添加的,而不是直接修改现有对象的行为,相当于增加了中间层。类似于python中的@装饰器。
下面还是按照老规矩,先来了解一下该模式相关的概念和原理,然后通过两个具体的实例体会一下如何在实际开发中应用该模式。
可以动态的为同一类的不同对象加以修饰以添加新的功能。
灵活的对类对象功能进行扩展。
优点:
缺点:
下面是GoF介绍的典型的装饰器模式的UML类图:
Component:
对象的接口类,定义装饰对象和被装饰对象的共同接口;
ConcreteComponent:
被装饰对象的类定义;
Decorator:
装饰对象的抽象类,持有一个具体的被修饰对象,并实现接口类继承的公共接口;
ConcreteDecorator:
具体的装饰器,负责往被装饰对象添加额外的功能;
说明:
由于这个模式从实际的例子来理解更加的直观方便,因此这里不再单独的实现上面的UML结构代码。
先来通过一个简单的画图的实例来直观感受一下。
前提:
系统中存在一个画圆的类,该类只是用来画圆,以及其他一些大小和位置等参数的控制。
新加需求:
这个需求的常规方法实现可能如下:
上面的两个方法都是可行的,也是比较直观的,这里我们尝试使用装饰器模式来实现,作为以上两种方法的对比。
下面来看一下装饰器模式实现该需求的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
上面我们通过实现两个装饰器分别完成对边着色和填充的需求,通过对装饰器的进一步装饰,我们完成了同时着色的需求。
接下来我们在使用网络数据传输的例子来体会一下装饰器模式,下图表示的是应用层的文件传输协议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传输了,无意义,也被必要。
其实所谓装饰器,本质上是对现有类对象的包裹,得到一个加强版的对象。
和python中@装饰器不同的是:
通过上面的两个例子,应该对装饰器模式有了一个简单的认识。
另外,要体会到什么时候用继承什么时候用装饰器。
参考:
GoF《Design Patterns: Elements of Reusable Object-Oriented Software》
https://www.runoob.com/design-pattern/decorator-pattern.html
原文:https://www.cnblogs.com/yssjun/p/11110013.html