装饰者模式

       装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使需要装饰的具体构件类和用于装饰的具体装饰类都可以独立变化,增加新的具体构件类和具体装饰类都非常方便,符合开闭原则。
       装饰模式的定义如下:
在这里插入图片描述       装饰模式是一种对象结构型模式,它以对客户透明的方式动态地给一个对象附加上更多的责任,可以在不需要创建更多子类的情况下让对象的功能得以扩展。

1.装饰模式结构

       装饰模式包含以下四个角色
       (1)Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象及装饰之后的对象,实现客户端的透明操作。
       (2)ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰类可以给他增加额外的职责(方法)。
       (3)Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现,并通过其子类扩展该方法,以达到装饰的目的。
       (4)ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用于扩充对象的行为。


图1.装饰模式结构图

2.装饰模式实现

       在装饰模式中,抽象构件类一般设计为抽象类或接口,在其中声明了抽象业务方法,也可以在抽象构件类中实现一些所有具体构建类都共有的业务方法。抽象构件类的典型代码如下:

package decoratorpattern;

public abstract class Component {
    public abstract void operation();
}

       具体构件类作为抽象构件类的子类实现了在抽象构件类中声明的业务方法,通常在具体构件类只提供基本功能的实现,一些复杂的功能需要通过装饰类进行扩展。其典型代码如下:

package decoratorpattern;

public class ConcreteComponent extends Component{

    @Override
    public void operation() {
        //基本功能的实现
    }
}

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

package decoratorpattern;

public class Decorator extends Component{
    private Component component;//维持一个对抽象构件对象的引用
    //注入一个抽象构件类型的对象
    public Decorator(Component component){
        this.component=component;
    }
    @Override
    public void operation() {
        component.operation();//调用原有的业务方法
    }

}

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

package decoratorpattern;

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

    }
}

       在具体装饰类中可以调用到抽象装饰类的operation方法,同时可以定义新的业务方法,例如addedBehavior()。如果该方法不希望客户端单独调用,可以将其可见性设为私有(private)。
       由于在抽象装饰类Decorator中注入的使Component类型的对象,因此可以将一个具体构件注入其中,再通过具体装饰类进行装饰;此外,还可以将一个已经装饰过的Decorator子类的对象再注入其中进行多次装饰,从而实现对原有功能的多次扩展。

2.装饰模式应用实例

在这里插入图片描述在这里插入图片描述


图2.图形界面构件库结构图
       在图2中,Component充当抽象构件类,其子类Window,TextBox,ListBox充当具体构件类,Component的另一个子类ComponentDecorator充当抽象装饰类,ComponentDecorator的子类ScrollBarDecorator和BlackBorderDecorator充当具体装饰类。

       (1)Component:抽象界面构件类,充当抽象构件类。

package decoratorpatternexample;

public abstract class Component {
    public abstract void display();
}

       (2)Window:窗体类,充当具体构件类。

package decoratorpatternexample;

public class Window extends Component{

    @Override
    public void display() {
        System.out.println("显示窗体");
    }
}

       (3)TextBox:文本框类,充当具体构件类。

package decoratorpatternexample;

public class TextBox extends Component{

    @Override
    public void display() {
        System.out.println("显示文本框");
    }
}

       (4)ListBox:列表框类,充当具体构件类。

package decoratorpatternexample;

public class ListBox extends Component{

    @Override
    public void display() {
        System.out.println("显示列表框");
    }
}

       (5)ComponentDecorator:构件装饰类,充当抽象装饰类。

package decoratorpatternexample;

public class ComponentDecoator extends Component{
        private Component component;//维持对抽象构件类型对象的引用
    public ComponentDecoator(Component component){
        this.component=component;
    }
    @Override
    public void display() {
            component.display();
    }
}

       (6)ScrollBarDecorator:滚动条装饰类,充当具体装饰类。

package decoratorpatternexample;

public class ScrollBarDecorator extends ComponentDecoator{

    public ScrollBarDecorator(Component component) {
        super(component);
    }
    public void display(){
        this.setScrollBar();
        super.display();
    }
    public void setScrollBar(){
        System.out.println("为构件增加滚动条");
    }
}

       (7)BlackBorderDecorator:黑色边框装饰类,充当具体装死类。

package decoratorpatternexample;

public class BlackBorderDecoator extends ComponentDecoator{

    public BlackBorderDecoator(Component component) {
        super(component);
    }
    public void display(){
        this.setBlackBorder();
        super.display();
    }
    public void setBlackBorder(){
        System.out.println("为构件增加黑色边框");
    }
}

       (8)Client:客户端测试类

package decoratorpatternexample;

public class Client {
    public static void main(String[] args){
        Component component,componentSB;//使用抽象构件定义对象
        component=new Window();//创建具体构件对象
        componentSB=new BlackBorderDecoator(component);//创建装饰后构件对象
        componentSB.display();
    }
}

       编译并运行程序,输出结果如下:
在这里插入图片描述       在客户端代码中先创建一个Window类型的具体构件对象component,然后将component作为构造函数的参数注入到具体装饰类ScrollBarDecorator中,得到一个装饰之后的对象componentSB,在调用componentSB的display()方法后将得到一个有滚动条的窗体。如果希望得到一个既有滚动条又有黑色边框的窗体,不需要对原有类库进行任何修改,只需将客户端代码修改如下:

package decoratorpatternexample;

public class Client1 {
    public static void main(String[] args){
        Component component,componentBB,componentSB;
        component=new Window();
        componentBB=new BlackBorderDecoator(component);
        componentSB=new ScrollBarDecorator(componentBB);
        componentSB.display();

    }

}

       再次编译并运行程序,输出结果如下:
在这里插入图片描述
       在上述客户端代码中将装饰了一次之后的componentBB对象再注入另一个装饰类ScrollBarDecorator中实现第二次装饰,得到一个经过两次装饰的对象componentSB,再调用componentSB的display()方法即可得到一个既有滚动条又有黑色边框的窗体。
       如果需要在原有系统中增加一个新的具体构件类或新的具体装饰类,无须修改现有类库代码,只需将它们风别作为抽象构件类和抽象装饰类的子类即可。

3.透明装饰模式与半透明装饰模式

       在装饰模式中,具体装饰类通过新增成员变量或成员方法来扩充具体构件类的功能。在标准的装饰模式中,新增行为须在原有业务方法中调用,无论是具体构件对象还是装饰过的构件对象,对于客户端而言都是透明的,这种装饰模式被称为透明装饰(Transparent)模式.但是在某些情况下,有些新增行为可能需要单调被调用,此时客户端不能再一致性地处理装饰之前的对象和装饰之后的对象,这种装饰模式被称为半透明(Semi-transparent)装饰模式。下面将对这两者装饰模式进行较为详细地介绍。

3.1透明装饰模式

       在透明装饰模式中要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。对于客户端而言,具体构件对象和具体装饰对象没有任何区别。应该使用如下代码:
在这里插入图片描述       而不应该使用以下代码:
在这里插入图片描述
在这里插入图片描述       对于多次装饰而言,在客户端中存在以下代码片段:
在这里插入图片描述       使用抽象构件类型Component定义全部具体构件对象和具体装饰对象,客户端可以一致地使用这些对象,因此符合透明装饰模式的要求。
       透明装饰模式可以让客户端透明地使用装饰之前的对象和装饰之后地对象,无须关系他们地区别,此外还可以对一个已装饰过的对象进行多次装饰得到更加复杂功能更加强大地对象。在实现透明装饰模式时要求具体装饰类的operation()方法覆盖抽象装饰类的operation()方法,除了调用原有对象的operation()外还需要调用新增的addedBehavior()方法来增加新的行为。但是由于在抽象构件中并没有声明addedbehavoir()方法,因此无法在客户端单调调用该方法,上节图形界面构件库的设计方案中使用的就是透明装饰模式。

3.2半透明装饰模式

       为了能够调用到新增方法,不得不用具体装饰类型来定义装饰之后的对象,而具体构件类型仍然可以使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式。对客户端而言具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的。客户端代码片段如下:
在这里插入图片描述       客户端需要区别地对待装饰之前的对象和装饰之后地对象。在实现半透明的装饰模式时只需在具体装饰类中增加一个独立的addedBehavior()方法来封装相应的业务处理,由于客户端使用具体装饰类型来定义装饰后的对象,因此可以单独调用addedBeahvior()方法。
       如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没必要建立一个单独的Decorator类,而可以把Decorator和ContreteDecorator类的责任合并成一个类。
       书中提到的其最大的缺点在于不能实现同时对一个对象的多次装饰,朕没想明白,翻看了其他基本设计模式书籍,也并未提及,如有小伙伴知道的话,欢迎在评论区留言,忠言也颇为顺耳。

4.真实世界的装饰者:Java I/O

在这里插入图片描述       编写一个装饰者,把输入流内的大写字符转换成小写字符。
       具体装饰者类如下所示:

package iodecoratorpattern;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class LowerCaseInputStream extends FilterInputStream {

    /**
     * Creates a {@code FilterInputStream}
     * by assigning the  argument {@code in}
     * to the field {@code this.in} so as
     * to remember it for later use.
     *
     * @param in the underlying input stream, or {@code null} if
     *           this instance is to be created without an underlying stream.
     */
    public LowerCaseInputStream(InputStream in) {
        super(in);
    }
    public int read() throws IOException {
        int c=super.read();
        return (c==-1?c:Character.toLowerCase((char) c));
    }
    public int read(byte[] b,int offset,int len) throws IOException {
        int result=super.read(b,offset,len);
        for(int i=offset;i<offset+result;i++){
            b[i]=(byte) Character.toLowerCase((char) b[i]);
        }
        return result;
    }
}

       客户端测试代码如下:

package iodecoratorpattern;

import java.io.*;

public class InputTest {
   public static void main(String[] args){
       int c;
       InputStream in;
       try {
           in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("inputtest.txt")));
           while ((c=in.read())>=0){
               System.out.print((char) c);
               if((char)c==' '){
                   System.out.println();
               }
           }
           in.close();
       } catch (IOException e) {
           e.printStackTrace();
       }
   }


}

       inputtest.txt内容如下:
在这里插入图片描述

       编译运行后,输出结果如下:
在这里插入图片描述       参考:Java设计模式(刘伟),Head First s设计模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值