三、装饰模式——设计模式学习笔记

来源:互联网 发布:ajaxupload.js cdn 编辑:程序博客网 时间:2024/05/22 07:03
作为一个编程菜鸟,过去在学习设计模式的时候,老师给推荐了一本《大话设计模式》。阅读以后受益匪浅,可惜当初没有坚持看完。最近有时间了,又重新捡起来学习了一遍,整理了一下笔记,由于本人能力有限,欢迎大家批评指正。

1.通过装饰模式,我们可以把所需的功能按正确的顺序串联起来进行控制。

2.装饰模式 Decorator Pattern

(1)概念

  • 装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
  • 在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。其设计原则是多用组合,少用继承。
  • 利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。
  • java IO流是典型的装饰模式

(2)特点

  • 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互
  • 装饰对象包含一个真实对象的引用(reference)
  • 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
  • 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现给定类的功能扩展。

2.unl类图

这里写图片描述

3.组成

(1)抽象构件(Component)

给出一个抽象接口,以规范准备接收附加责任的对象。

(2)具体构件(Concrete Component)角色

定义一个将要接收附加责任的类。

(3)装饰(Decoretor)角色

持有一个构件对象的实例,并实现一个与抽象构件接口一致的接口。

(4)具体装饰(Concrete Decorator)角色

负责给构件对象添加上附加的责任。

4.优缺点

(1)优点

  • Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

(2)缺点

  • 这种比继承更加灵活机动的特性,也同时意味着更多的复杂性。
  • 装饰模式会导致设计中出现许多小类,如果过度使用会使程序变得很复杂。
  • 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

5.适用性

  • 需要扩展一个类的功能,或给一个类添加附加职责。
  • 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
  • 需要增加由一些基本功能的排列组合而组合而产生的非常大量的功能,从而使继承关系变的不现实。
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸式增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

6.实例

(1)需求

按顺序实现穿衣打扮。

(2)实例结构图

这里写图片描述

(3)代码

a.人具体构件类
package com.longinus.dp;public class Person {    public void show(){        System.out.println("的打扮");    }}
b.服饰装饰类
package com.longinus.dp;public class Dress extends Person{    protected Person component;    public void decorate(Person component){        this.component = component;    }    @Override    public void show() {        // TODO 自动生成的方法存根        if(component != null){            component.show();        }    } }
c.具体服饰装饰类
package com.longinus.dp;public class TShit extends Dress{    @Override    public void show() {        // TODO 自动生成的方法存根        System.out.print("T恤");        super.show();    }    }
package com.longinus.dp;public class Pants extends Dress{    @Override    public void show() {        // TODO 自动生成的方法存根        System.out.print("紧身裤");        super.show();    }}
package com.longinus.dp;public class Boots extends Dress{    @Override    public void show() {        // TODO 自动生成的方法存根        System.out.print("鞋子");        super.show();    } }
d.测试类
package com.longinus.dp;public class Test {    public static void main(String[] args) {        Person p = new Person();        TShit ts = new TShit();        Pants ps = new Pants();        Boots bs = new Boots();        bs.decorate(p);        ps.decorate(bs);        ts.decorate(ps);        ts.show();        ts.decorate(p);        ps.decorate(ts);        bs.decorate(ps);        bs.show();    }}
e.执行结果
T恤紧身裤鞋子的打扮鞋子紧身裤T恤的打扮

7.总结

  • 装饰模式是为已有功能动态地添加更多功能的一种方式
  • 当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为。
  • 在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
  • 装饰模式把类中的装饰功能从类中搬移去除,有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。

8.装饰者模式与适配器模式的区别

(1)关于新职责

适配器也可以在转换时增加新的职责,但主要目的不在此。装饰者模式主要是给被装饰者增加新职责的。

(2)关于原接口

适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。(增加新接口的装饰者模式可以认为是其变种-“半透明”装饰者)

(3)关于其包裹的对象

适配器是知道被适配器者的详细情况的(就是那个类或那个接口)。装饰者只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道。