1、简介
定义:装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
拆分定义,总结特点:
1、不改变原类文件。
2、不使用继承。
3、动态扩展。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
英文:Decrator
类型:结构型模式
2、类图及组成
(引)类图:
角色:
●抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
●具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
●装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
●具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
代码结构:
抽象构件角色 public interface Component { public void sampleOperation(); } 具体构件角色 public class ConcreteComponent implements Component { @Override public void sampleOperation() { // 写相关的业务代码 } } 装饰角色 public class Decorator implements Component{ private Component component; public Decorator(Component component){ this.component = component; } @Override public void sampleOperation() { // 委派给构件 component.sampleOperation(); } } 具体装饰角色 public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } @Override public void sampleOperation() { super.sampleOperation(); // 写相关的业务代码 } } 具体装饰角色 public class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } @Override public void sampleOperation() { super.sampleOperation(); // 写相关的业务代码 } }
3、实例引入
本例中 抽象构建角色由Coder程序员接口扮演
具体构件角色由类SpecificCoder扮演
装饰构件由类Derector扮演,它必须也实现抽象构件接口
具体装饰构件角色由类JavaCoder(程序员)和类 JavaArchitect(架构师)扮演
具体程序员xxx有编程能力,通过学习和实践,他渐渐的获得很多种能力,每多一种能力,都通过装饰类实现的
package com.designpattern.Decorator; /** * 抽象构建角色 * 程序员接口 * @author Json */ public interface Coder { /** * 写代码 */ public void writeCode(); }
具体构建角色:
package com.designpattern.Decorator; /** * 具体构建角色 * 一个具体的程序员 * @author Json */ public class SpecificCoder implements Coder { @Override public void writeCode() { System.out.println("我是一个程序员,我能写代码..."); } }
一个具体的装饰角色:
package com.designpattern.Decorator; /** * 一个具体的装饰角色 * java普通程序员 * @author Json */ public class JavaCoder extends Decorator { public JavaCoder(Coder coder) { super(coder); } @Override public void writeCode(){ super.writeCode(); //可以追加功能 System.out.println("我是Java程序员..."); } }
一个具体的装饰角色:
package com.designpattern.Decorator; /** * 一个具体的装饰角色 * java架构师 * @author Json */ public class JavaArchitect extends Decorator { public JavaArchitect(Coder coder) { super(coder); } @Override public void writeCode(){ super.writeCode(); //可以追加功能 System.out.println("我是Java架构师..."); } }
测试:
package com.designpattern.Decorator; /** * 测试 * @author Json */ public class Test { public static void main(String[] args) { Coder coder = new SpecificCoder(); System.out.println("第一次装饰 ↓↓↓"); Decorator javacoder = new JavaCoder(coder); javacoder.writeCode(); System.out.println("第二次装饰 ↓↓↓"); Decorator javaarchitect = new JavaArchitect(javacoder); javaarchitect.writeCode(); System.out.println("------------------------------------"); System.out.println("一步装饰多次 ↓↓↓"); //一步装饰多次 Decorator javaarchitect_1 = new JavaArchitect(new JavaCoder(new SpecificCoder())); javaarchitect_1.writeCode(); } }
结果:
第一次装饰 ↓↓↓ 我是一个程序员,我能写代码... 我是Java程序员... 第二次装饰 ↓↓↓ 我是一个程序员,我能写代码... 我是Java程序员... 我是Java架构师... ------------------------------------ 一步装饰多次 ↓↓↓ 我是一个程序员,我能写代码... 我是Java程序员... 我是Java架构师...
4、优缺点
优点:
1、 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
2、 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
3、 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
4、 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
缺点:
1、 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
2、 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
5、应用场景
1、 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的 扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如 Java 语言中的 final 类)。
6、装饰者模式与继承
1、装饰模式是一种动态行为,对已经存在类进行随意组合,而类的继承是一种静态的行为,一个类定义成什么样的,该类的对象便具有什么样的功能,无法动态的改变。
2、装饰模式扩展的是对象的功能,不需要增加类的数量,而类继承扩展是类的功能,在继承的关系中,如果我们想增加一个对象的功能,我们只能通过继承关系,在子类中增加方法。
3、装饰模式是在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能,它是通过创建一个包装对象,也就是装饰来包裹真是的对象。
PS:一定情况下,可代替继承。
7、与其他模式对比
装饰者模式是在稳定接口上扩展:
原有的不能满足现有的需求,对原有的进行增强。
不会改变接口,而是将一个一个的接口进行装饰,也就是添加新的功能。
装饰器模式特点在于增强,他的特点是被装饰类和所有的装饰类必须实现同一个接口,而且必须持有被装饰的对象,可以无限装饰。
适配器模式是接口的转换:
适配器的特点在于兼容,从代码上的特点来说,适配类与原有的类具有相同的接口,并且持有新的目标对象。
在使用适配器模式的时候,我们必须同时持有原对象,适配对象,目标对象......
代理模式是封装对象的访问过程:
代理模式的特点在于隔离,隔离调用类和被调用类的关系,通过一个代理类去调用。
8、总结
装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体 装饰类。
在软件开发中,装饰模式应用较为广泛,例如:
装饰者模式在JDK中的运用:
Java当中的 IO中输入流和输出流 是运用了装饰者模式的最典型的例子。
下面是一个简单的例子,通过BufferedReader对象来装饰InputStreamReader对象:
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
//System.in is an InputStream object
其他场景:
Swing包中图形界面构件功能
Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,增强了request对象的功能。
Struts2中,request,response,session对象的处理。
PS:源码地址 https://github.com/JsonShare/DesignPattern/tree/master