一文弄懂23种设计模式之装饰器模式

一文弄懂23种设计模式之装饰器模式,第1张

一文弄懂23种设计模式之装饰器模式 前言

装饰器模式 Reference

[1] bugstack.cn/md/develop/…

[2] c.biancheng.net/view/1397.h…

[3] refactoringguru.cn/design-patt…

[4] cmsblogs.com/article/140…

[5] blog.csdn.net/lovelion

什么是装饰器模式

装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

场景

假设你正在开发一个提供通知功能的库, 其他程序可使用它向用户发送关于重要事件的通知。

最初的版本基于 Notifier 类,用来接受客户端消息参数并发送给一系列邮箱,邮箱列表通过构造函数传递。

后续有新的需求增加, 用户要求能够在手机短信、微信、QQ上接收消息,那么可以这样做,扩展通知类,在子类中加入额外的通知方法,然后组合多种通知方法即可解决问题

弊端
  • 系统扩展麻烦,在某些编程语言中无法实现,Java 中是不支持多继承的,所有每次要扩展一项功能,就必须新增一个类
  • 代码重复,可以发现发送消息的代码在多个类中重复
  • 系统庞大,类的数目非常多

解决方案

根本原因在于,这个设计的复用机制不合理,上图采用的是继承复用的机制,而我们知道,根据设计原则,应该多用组合,少用继承。

  • 继承是静态的。 你无法在运行时更改已有对象的行为, 只能使用由不同子类创建的对象来替代当前的整个对象。
  • 子类只能有一个父类。 大部分编程语言不允许一个类同时继承多个类的行为。

因此我们可以换个角度,将 send 方法这个通知行为放在基类通知器中,将所有其他方法放入装饰中。

这样,客户端代码就可以以装饰器的模式封装自己想要的功能

装饰模式结构 结构1
  • 单个具体构件
  • 没有抽象构件

如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件,也就是上面的 Notifier 的例子

结构2
  • 只有一个具体装饰

如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并

结构3

这个是经典的装饰器结构

  • 有多个具体构件类
  • 又有抽象构件类

  1. 部件,也叫抽象构件 (Component) 声明封装器和被封装对象的公用接口。
  2. 具体部件,也叫具体构件 (Concrete Component) 类是被封装对象所属的类。 它定义了基础行为, 但装饰类可以改变这些行为。
  3. 基础装饰 (base Decorator) 类拥有一个指向被封装对象的引用成员变量。 该变量的类型应当被声明为通用部件接口, 这样它就可以引用具体的部件和装饰。 装饰基类会将所有 *** 作委派给被封装的对象。
  4. 具体装饰类 (Concrete Decorators) 定义了可动态添加到部件的额外行为。 具体装饰类会重写装饰基类的方法, 并在调用父类方法之前或之后进行额外的行为。
  5. 客户端 (Client) 可以使用多层装饰来封装部件, 只要它能使用通用接口与所有对象互动即可。
核心设计

装饰模式的核心在于抽象装饰类的设计,其典型代码如下所示:

class Decorator implements Component
{
    //维持一个对抽象构件对象的引用
    private Component component;  

    //注入一个抽象构件类型的对象
    public Decorator(Component component)  {
        this.component=component;
    }
    
    public void operation( ) {
        component.operation( );  //调用原有业务方法
    }
}
复制代码

在抽象装饰类Decorator中定义了一个Component类型的对象component,维持一个对抽象构件对象的引用,并可以通过构造方法或Setter方法将一个Component类型的对象注入进来,同时由于Decorator类实现了抽象构件Component接口,因此需要实现在其中声明的业务方法operation(),需要注意的是在Decorator中并未真正实现operation()方法,而只是调用原有component对象的operation()方法,它没有真正实施装饰,而是提供一个统一的接口,将具体装饰过程交给子类完成。

在Decorator的子类即具体装饰类中将继承operation()方法并根据需要进行扩展,典型的具体装饰类代码如下:

class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component) {
        super(component);

    }

    public void operation() {
        super.operation();  //调用原有业务方法
        addedBehavior();  //调用新增业务方法

    }

    //新增业务方法
    public void addedBehavior() {
        ……
    }

}
复制代码

在具体装饰类中可以调用到抽象装饰类的operation()方法,同时可以定义新的业务方法,如 addedBehavior()

由于在抽象装饰类Decorator中注入的是Component类型的对象,因此我们可以将一个具体构件对象注入其中,再通过具体装饰类来进行装饰;此外,我们还可以将一个已经装饰过的Decorator子类的对象再注入其中进行多次装饰,从而对原有功能的多次扩展。

案例设计

Sunny软件公司基于面向对象技术开发了一套图形界面构件库VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特效显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能

直接利用装饰器模式,进行系统设计,使系统具有更好的灵活性和扩展性

具体代码
//抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大量的简化
abstract class Component {
    public abstract void display();
}

//窗体类:具体构件类
class Window extends Component {
    public void display() {
        System.out.println("显示窗体!");
    }
}

//文本框类:具体构件类
class TextBox extends Component {
    public void display() {
        System.out.println("显示文本框!");
    }
}

//列表框类:具体构件类
class ListBox extends Component {
    public void display() {
        System.out.println("显示列表框!");
    }
}


//构件装饰类:抽象装饰类
class ComponentDecorator extends Component {

    private Component component;  //维持对抽象构件类型对象的引用

    //注入抽象构件类型的对象
    public ComponentDecorator(Component component)  {
        this.component = component;
    }

    public void display() {
        component.display();
    }

}

//滚动条装饰类:具体装饰类
class ScrollBarDecorator extends ComponentDecorator {

    public ScrollBarDecorator(Component component) {
        super(component);
    }

    public void display() {
        this.setScrollBar();
        super.display();
    }

    public void setScrollBar() {
        System.out.println("为构件增加滚动条!");
    }

}


//黑色边框装饰类:具体装饰类
class BlackBorderDecorator extends ComponentDecorator {

    public BlackBorderDecorator(Component component) {
        super(component);
    }

    public void display() {
        this.setBlackBorder();
        super.display();
    }

    public void setBlackBorder() {
        System.out.println("为构件增加黑色边框!");
    }

}
复制代码
客户端代码
class Client{
    public  static void main(String args[])  {
        Component  component,componentSB,componentBB; //全部使用抽象构件定义
        component = new Window();
        componentSB = new  ScrollBarDecorator(component);
        //将装饰了一次之后的对象继续注入到另一个装饰类中,进行第二次装饰
        componentBB = new  BlackBorderDecorator(componentSB); 
        componentBB.display();
    }
    
    // 也可以这样写
    Component  component;
    component =  new  BlackBorderDecorator(  new  ScrollBarDecorator( new Window( )));
}
复制代码

装饰器模式在 Java 语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。

BufferedReader in = new BufferedReader(new FileReader("filename.txt"));
String s = in.readLine();
复制代码
小结

装饰器模式的主要优点有:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5672465.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存