结构模式之装饰模式

视频讲解装饰模式链接:https://www.bilibili.com/video/BV1Re411x7Ek/


 

一、装饰模式概述

  装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在设计中用得还是比较多的。在现实生活中,这种情况也到处存在,例如下面一张猫咪的照片,我们可以不改变照片本身,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小相框的外面再套一个大相框。

                                                        

 

装饰模式的模式动机:

装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。

 

装饰模式分析:

  1. 可以在不改变一个对象本身功能的基础上给对象增加额外的新行为
  2. 是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系
  3. 引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能。

装饰模式定义:动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。

装饰模式:是一种对象结构型模式;以对客户透明的方式动态地给一个对象附加上更多的责任;可以在不需要创建更多子类的情况下,让对象的功能得以扩展。

 

装饰模式的结构如下图:

                                                            

 

 

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

public class Decorator extends Component
{
    private Component component;
    public Decorator(Component component)
    {
        this.component=component;
    }
    public void operation()
    {
        component.operation();  //调用原有业务方法
    }
} 

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

public class ConcreteDecorator extends Decorator
{
    public ConcreteDecorator(Component component)
    {
        super(component);
    }
    public void operation()
    {
        super.operation();    //调用原有业务方法
        addedBehavior();    //调用新增业务方法
    }
    public void addedBehavior()
    {
                  //新增业务方法    
            }
} 

装饰模式包含四个角色:

  • Component: 抽象构件角色;它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
抽象构件类典型代码:
abstract class Component
{
    public abstract void Operation();  
}
  • ConcreteComponent: 具体构件角色;它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)
class ConcreteComponent:Component
{
    public void Operation()
    {
        //基本功能实现  
    }
}    
  • Decorator: 抽象装饰类;它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的
class Decorator implements Component
{
       private Component component;  //维持一个对抽象构件对象的引用
       public Decorator(Component component)  //注入一个抽象构件类型的对象
       {
              this.component=component;
       }
       public void Operation()
       {
              component.Operation();  //调用原有业务方法
       }
}
  • ConcreteDecorator: 具体装饰类;它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为
class ConcreteDecorator extends Decorator
{
       public ConcreteDecorator(Component component)
       {
              super(component);
       }
       public void operation()
       {
              super.operation();  //调用原有业务方法
              addedBehavior();  //调用新增业务方法
       }

 //新增业务方法
  public  void addedBehavior()
     {    
         //功能扩展
     }
}

 

二、举例说明

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

                                              

实例代码:

VisualComponent: 抽象界面构建类,充当抽象构件类;

Window: 窗体类,充当具体构件类;

TextBox: 文本框类,充当具体构件类;

ListBox: 列表框类,充当具体构件类;

ComponentDecorator: 构件装饰类,充当抽象构件类;

ScrollBarDecorator: 滚动条装饰类,充当具体构件类;

BlackBorderDecorator: 黑色边框装饰类,充当具体构件类;

Client: 客户端测试类。

//抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大量的简化
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;  //使用抽象构件定义
              component = new Window(); //定义具体构件
              componentSB = new  ScrollBarDecorator(component); //定义装饰后的构件
              componentSB.display();
       }
}


运行程序后的输出结果如下:

为构件增加滚条...
显示窗体...

三、装饰模式优缺点

优点:

1.对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加;

2.可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为;

3.可以对一个对象进行多次装饰;

4.具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则。

 

缺点:

1.比继承容易出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐;

2.复杂,难管理,难阅读:使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。

 

使用装饰模式的场景:

(1)需要扩展一个类的功能,或给一个类增加附加责任。
(2)需要动态地给一个对象增加功能,这个功能可以再动态地撤销。
(3)需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
 
posted @ 2020-04-08 10:31  yeungYeung洋  阅读(283)  评论(0)    收藏  举报