首页 > 代码库 > 装饰者模式

装饰者模式

装饰者模式

标签 : Java与设计模式


装饰者模式(Decorator): 又称包装器(Wrapper), 可以动态地为一个对象添加一些额外的职责. 就增加功能来说, 装饰者模式是一种用于替代继承的技术, 他无须通过增加子类继承就能扩展对象的已有功能, 而是使用对象的关联关系代替继承关系 , 更加灵活, 同时还可避免类型体系的快速膨胀.

  • 模式组件:
组件 描述 I/O示例
Component 抽象构件角色, 真实对象和装饰对象的共有接口. 这样,客户端就能以调用真实对象的相同方式装饰对象交互. InputStream/OutputStream
ConcreteComponent 具体构件角色,真实对象 FileInputStream/FileOutputStream
Decorator 装饰抽象类, 实现了Component, 并持有一个Component引用, 接受所有客户端请求,并将请求转发给真实对象, 这样,就能在真实对象调用的前后增强新功能. 但对于Component来说, 是无需知道Decorator存在的. FilterInputStream/FilterOutputStream
ConcreteDecorator 具体装饰角色,完成对Component的具体增强. BufferedInputStream/BufferedOutputStream

技术分享

是你还有你, 一切拜托你.(图片来源: 《JAVA与模式》之装饰模式)


实现

  • Component
/**
 * @author jifang
 * @since 16/8/20 下午5:55.
 */
public interface Component {

    void operator();
}

class ConcreteComponent implements Component {

    @Override
    public void operator() {
        System.out.println("具体对象" + this.toString() + "的操作");
    }
}
  • Decorator
public abstract class Decorator implements Component {

    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public abstract void operator();
}

class BeforeAdviceDecorator extends Decorator {

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

    @Override
    public void operator() {
        System.out.println(" -> 前置增强");
        this.component.operator();
    }
}

class AfterAdviceDecorator extends Decorator {

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

    @Override
    public void operator() {
        this.component.operator();

        System.out.println("后置增强 -> ");
    }
}
  • Client
public class Client {

    @Test
    public void client() {

        // 裸Component
        Component component = new ConcreteComponent();
        component.operator();


        // 前置增强
        component = new BeforeAdviceDecorator(component);
        component.operator();

        // + 后置增强
        component = new AfterAdviceDecorator(component);
        component.operator();
    }
}

注: 如果只有ConcreteComponent而没有抽象的Component, 那么Decorator可直接继承ConcreteComponent. 同样, 如果只有一个ConcreteDecorator, 那就没有必要建立一个独立的Decorator, 将DecoratorConcreteDecorator的职责合并.


小结

  • 装饰者模式是为已有功能动态添加更多功能的一种方式: 把类内装饰逻辑从类中剥离, 以简化原有类设计:
    • 有效地将类的核心职责和装饰功能区分开;
    • 去除相关类中重复的装饰逻辑;
    • 可以对一个对象装饰多次, 构造出不同行为的组合, 得到功能更强大的对象;
    • 具体构件类和具体装饰类可独立变化, 且用户可根据需要增加新的具体构件子类具体装饰子类.
  • 与桥接模式的对比
    两个模式都是为了解决子类过多问题, 但他们的诱因不同:

    • 桥接模式对象自身有沿着多个维度变化的趋势, 本身不稳定;
    • 装饰者模式对象自身非常稳定, 只是为了增加新功能/增强原功能.
  • 继承、装饰者模式、动态代理对比

* 继承 装饰者 动态代理
对象 被增强对象不能变 被增强对象可变 被增强对象可变
内容 增强内容不能变 增强内容不可变 增强内容可变
  • 常见场景
    当系统更新、原有逻辑需要增强时, 我们最初的想法是 向旧的类中添加新代码, 由新代码装饰原有类的主要行为, 他们会在主类中加入新的字段/方法/逻辑, 但同样也增加了主类的复杂度, 而这些新加入的内容仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要. 而装饰者模式提供了一个解决该问题的非常好的方案: 把每个要装饰的功能放在单独的类中, 并让这个类包装它所要装饰的对象, 当需要执行特殊行为时, 客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰过的包装对象了, 如:
    • Java I/O流体系(详细可参考: Java I/O);
    • Servlet API: HttpServletRequestWrapper/HttpServletResponseWrapper增强Servlet功能(详细可参考Servlet - Listener、Filter、Decorator)

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    装饰者模式