面向对象设计模式之结构型模式(一):装饰者模式
李青原(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合适的装饰类,用装饰类来读取。