设计模式之装饰器模式(decorator pattern)

装饰器模式主要对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。是一种对象结构型模式。需要注意的是,该过程是通过调用被包裹之后的对象完成功能添加的,而不是直接修改现有对象的行为,相当于增加了中间层。类似于python中的@装饰器。

下面还是按照老规矩,先来了解一下该模式相关的概念和原理,然后通过两个具体的实例体会一下如何在实际开发中应用该模式。

1. 目的

可以动态的为同一类的不同对象加以修饰以添加新的功能。

2. 动机

灵活的对类对象功能进行扩展。

3. 优缺点

优点:

  1. 相比较于类的继承来扩展功能,对对象进行包裹更加的灵活;
  2. 装饰类和被装饰类相互独立,耦合度较低;

缺点:

  1. 没有继承结构清晰;
  2. 包裹层数较多时,难以理解和管理;

4. 应用场景

  • 动态的增加对象的功能;
  • 不能以派生子类的方式来扩展功能;
  • 限制对象的执行条件;
  • 参数控制和检查等;

5.  原理

下面是GoF介绍的典型的装饰器模式的UML类图:

Component:

 对象的接口类,定义装饰对象和被装饰对象的共同接口;

ConcreteComponent:

 被装饰对象的类定义;

Decorator:

 装饰对象的抽象类,持有一个具体的被修饰对象,并实现接口类继承的公共接口;

ConcreteDecorator:

 具体的装饰器,负责往被装饰对象添加额外的功能;

说明:

 由于这个模式从实际的例子来理解更加的直观方便,因此这里不再单独的实现上面的UML结构代码。

6.实例——画图

先来通过一个简单的画图的实例来直观感受一下。

前提:

系统中存在一个画圆的类,该类只是用来画圆,以及其他一些大小和位置等参数的控制。

新加需求:

  • 可以对圆的边进行着色
  • 可以对圆填充颜色;
  • 可以同时对边和内部着色;

这个需求的常规方法实现可能如下:

  1. 对画圆类进行迭代,以支持边和内部颜色填充 ;
  2. 画圆类作为父类,分别定义三个子类,继承父类的画圆方法,子类分别实现对应的作色需求;

上面的两个方法都是可行的,也是比较直观的,这里我们尝试使用装饰器模式来实现,作为以上两种方法的对比。

下面来看一下装饰器模式实现该需求的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中@装饰器不同的是:

  1. python中的装饰器是作用于函数或者类定义的,并直接覆盖掉了原来函数或者类的定义;
  2. 装饰器模式仅仅是修改了了已经产生的对象的行为,和类定义没有半点关系;

通过上面的两个例子,应该对装饰器模式有了一个简单的认识。

另外,要体会到什么时候用继承什么时候用装饰器。

参考:

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

时间: 2024-10-29 03:20:55

设计模式之装饰器模式(decorator pattern)的相关文章

23种设计模式之装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能. 通过采用组合.而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能.避免了单独使用继承带来的“灵活性差"和"多子类衍生问题". 优点:装饰类和被装饰类可以独立发

设计模式(三)——装饰器模式(Decorator Pattern)

发现太过于刻意按照计划来写博客,有点不实际,刚好最近在一个网课上复习AOP的知识,讲到了装饰器模式和代理模式,顺便复习总结一下. 首先了解一下装饰器模式,从名字里面可以看出来,装饰器模式就类似于房子装潢吧,比如刚买的毛坯房,只有一个没有门,直接就可以进去. 首先设计一个房子类Room,实现一个进入方法Access /// <summary> /// 抽象接口 用来进行约束 /// </summary> public interface IAccess { void Access()

用最简单的例子理解装饰器模式(Decorator Pattern)

假设有一个公司要做产品套餐,即把不同的产品组合在一起,不同的组合对应不同的价格.最终呈现出来的效果是:把产品组合的所有元素呈现出来,并显示该组合的价格. 每个产品都有名称和价格,首先设计一个关于产品的抽象基类. public abstract class ProductBase { public abstract string GetName(); public abstract double GetPrice(); } 所有的产品都必须继承这个基类,比如家居用品.电器产品等,把这些具体的产品提

php装饰器模式(decorator pattern)

十一点了. <?php /* The decorator pattern allows behavior to be added to an individual object instance, without affecting the behavior of other instances of the same class. We can define multiple decorators, where each adds new functionality. */ interface

设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 使用方法

装饰者模式(Decorator Pattern) Java的IO类 使用方法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716823 装饰者模式(decorator pattern)参见: http://blog.csdn.net/caroline_wendy/article/details/26707033 Java的IO类使用装饰者模式进行扩展, 其中FilterInputStream类, 就是装饰者(decora

设计模式 - 装饰者模式(Decorator Pattern) 详解

装饰者模式(Decorator Pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者模式(Decorator Pattern):动态地将责任附加到对象上. 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案. 使用方法: 1. 首先创建组件(Component)父类, 所有类,具体组件(Concrete Component)和装饰者(Decorator)都属于这一类型, 可以进行扩展

设计模式(八)装饰器模式Decorator(结构型)

设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上. 通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法.但是这种方法是静态的,用户不能控制增加行为的方式和时机.如果  你希望改变一个已经初始化的对象的行为,你怎么办?或者,你希望继承许

说说设计模式~装饰器模式(Decorator)

装饰器模式,也叫又叫装饰者模式,顾名思义,将一个对象进行包裹,包装,让它变成一个比较满意的对象,这种模式在我们平时项目开发中,经常会用到,事实上,它是处理问题的一种技巧,也很好的扩展了程序,让程序代码不那么死板! 何时能用到它? 1. 需要扩展一个类的功能,或给一个类添加附加职责. 2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销. 3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实. 4. 当不能采用生成子类的方法进行扩充时. 其中我们认为第四种

设计模式 - 装饰者模式(Decorator Pattern) 具体解释

装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者模式(Decorator Pattern):动态地将责任附加到对象上. 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案. 用法: 1. 首先创建组件(Component)父类, 全部类,详细组件(Concrete Component)和装饰者(Decorator)都属于这一类型, 能够进行扩展

设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法

装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716823 装饰者模式(decorator pattern)參见: http://blog.csdn.net/caroline_wendy/article/details/26707033 Java的IO类使用装饰者模式进行扩展, 当中FilterInputStream类, 就是装饰者(decorato