面向对象设计模式之结构型模式(一):装饰者模式

李青原(liqingyuan1986@aliyun.com)创作于博客园个人博客(http://www.cnblogs.com/liqingyuan/),转载请标明出处。

A.UML:

 

(注:有时Component会设计为接口,此时具体类与其关系为实现)

 

B.代码实现:

基类与接口实现代码:

public interface Component {
    public void work();
}
public abstract class Decorator implements Component{
    
    protected Component component;
    
    protected Decorator(Component component){
        this.component = component;
    }
}

 

具体实现代码:

public class ConcreteComponent implements Component {

    @Override
    public void work() {
        System.out.println("主功能实现");
    }

}
public class ConcreteDecoratorA extends Decorator {

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

    @Override
    public void work() {
        System.out.println("装饰功能A");
        component.work();
    }

}
public class ConcreteDecoratorB extends Decorator {

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

    @Override
    public void work() {
        System.out.println("装饰功能B");
        component.work();
    }

}

测试代码:

public class test {

    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Decorator decoratedComponent1 = new ConcreteDecoratorA(component);
        Decorator decoratedComponent2 = new ConcreteDecoratorB(component);
        Decorator decoratedComponent3 = new ConcreteDecoratorA(new ConcreteDecoratorB(component));
        Decorator decoratedComponent4 = new ConcreteDecoratorB(new ConcreteDecoratorA(component));
        
        decoratedComponent1.work();
        decoratedComponent2.work();
        decoratedComponent3.work();
        decoratedComponent4.work();
    }

}

 

3.模式分析:

结构型模式针对的是多个类之间的组织关系,也就是如何处理多态。

最常见的多态实现方式,自然是继承。但是静态语言的继承存在2个缺点:

  (1)继承是静态的,子类对父类的扩展一旦被确定,就无法修改;

  (2)继承是低效率的,任何子类对父类的扩展,只有在子类被使用时才有意义。如果大量存在被使用次数非常低的子类,则意味着我们为了解决“少量需求”使用了“大量代码”。

在JAVA中,有时为了解决上面的问题,我们会采用匿名内部类的方式来处理那些“一次性多态需求”——不写具体子类,当需要时临时给JVM一个子类对象,用完就丢。

但是这样会出现新的问题——我们确定当前是“一次性”的需求,在未来依然如此吗?如果在新功能中我们需要再次使用原来的扩展,就必须写一个一模一样的匿名内部类,这将造成冗余并且增加修改成本。

装饰者模式为解决这个问题提供了一个好的思路:把基本功能和扩展功能区分开,并把扩展功能组件化,在使用者中按需组装成需要的扩展子类

在代码实现中,ConcreteComponent负责真正的核心功能,而那些可有可无的次要功能则被封装到Decorator的子类中。我们每需要一个功能,就在核心类上套一个装饰类,就像测试代码中那样。

 

4.优缺点:

优点有:灵活性——核心功能与扩展功能分离,不仅扩展功能可选,使得核心功能也可以有多个实现版本,使用者按需选择。

    高效性——如果扩展功能有m个,为了实现全部多态所需要的传统子类个数是一个排序数之合A(m,m)+A(m-1,m)+...+A(1,m),而使用装饰者模式,我们只需要m个扩展装饰类就行了。

缺点有:组件必须分离——由于扩展功能必须能够被随意顺序添加,我们不仅要保证核心功能与扩展功能不相互依赖,更要保证扩展功能之间不相互依赖。

    核心功能复杂——不难想象,核心功能类越复杂,在其上添加完全分离的扩展功能就越难。如果核心功能复杂到一定程度,装饰类将很难实现。

 

5.使用场景:

装饰者模式最经典的使用场景之一就是JAVA的IO包:InputStream,OutputStream,Writer,Reader 4个类的基础上,JAVA建立了分别针对字节流、字符流的读写功能。

下面是java.io包中基于InputStream的类UML图:

InputStream为基类,左侧4个类是单独针对特殊需求的实现,而右侧则是针对普通需求设计的类。很明显右侧使用了装饰者模式,ByteArrayInputStream和FileInputStream是2个版本的核心功能,分别针对字节数组和文件;而FilterInputStream则是所有装饰类的父类,4个子类分别对应了JAVA基本类读取、带缓存的读取、按行读取、数据推回等可选功能。

因此可以清晰的了解到字符流读取的使用方式:可以直接new一个ByteArrayInputStream或者FileInputStream对象来读取,也可以将其作为参数再new合适的装饰类,用装饰类来读取。

posted @ 2013-05-26 23:30  李青原  阅读(456)  评论(0编辑  收藏  举报