1.什么是装饰模式
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式
举个例子吧,咖啡是一种饮料,咖啡的本质是咖啡豆+水磨出来的。咖啡店现在要卖各种口味的咖啡,如果不使用装饰模式,那么在销售系统中,各种不一样的咖啡都要产生一个类,如果有4中咖啡豆,5种口味,那么将要产生至少20个类(不包括混合口味),非常麻烦。使用了装饰模式,只需要11个类即可生产任意口味咖啡(包括混合口味)。
2.实现方式
1).透明模式
package cn.limbo.design_patterns.decorator;
/**
* 抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大量的简化
* Created by limbo on 2016/12/6.
*/
public abstract class Component {
public abstract void display();
}
Window.java
package cn.limbo.design_patterns.decorator.base_component;
import cn.limbo.design_patterns.decorator.Component;
/**
* 基础的窗体类,实现了抽象的构件
* Created by limbo on 2016/12/6.
*/
public class Window extends Component {
@Override
public void display() {
System.out.println("显示窗体!");
}
}
ListBox.java
package cn.limbo.design_patterns.decorator.base_component;
import cn.limbo.design_patterns.decorator.Component;
/**
* 基础的列表框类,实现了抽象的构件
* Created by limbo on 2016/12/6.
*/
public class ListBox extends Component {
@Override
public void display() {
System.out.println("显示列表框!");
}
}
TextBox.java
package cn.limbo.design_patterns.decorator.base_component;
import cn.limbo.design_patterns.decorator.Component;
/**
* 基础的文本框类,实现了抽象的构件
* Created by limbo on 2016/12/6.
*/
public class TextBox extends Component {
@Override
public void display() {
System.out.println("显示文本框!");
}
}
ComponentDecorator.java
package cn.limbo.design_patterns.decorator;
/**
* 构件装饰类:抽象装饰类
* Created by limbo on 2016/12/6.
*/
public class ComponentDecorator extends Component {
//维持对抽象构件类型对象的引用
private Component component;
//注入抽象构件类型的对象
public ComponentDecorator(Component component) {
this.component = component;
}
@Override
public void display() {
component.display();
}
}
BlackBorderDecorator.java
package cn.limbo.design_patterns.decorator.decorator_component;
import cn.limbo.design_patterns.decorator.Component;
import cn.limbo.design_patterns.decorator.ComponentDecorator;
/**
* 黑色边框装饰类:具体装饰类
* Created by limbo on 2016/12/6.
*/
public class BlackBorderDecorator extends ComponentDecorator {
public BlackBorderDecorator(Component component) {
super(component);
}
@Override
public void display() {
this.setBlackBorder();
super.display();
}
public void setBlackBorder() {
System.out.println("为构件增加黑色边框!");
}
}
ScrollBarDecorator.java
package cn.limbo.design_patterns.decorator.decorator_component;
import cn.limbo.design_patterns.decorator.Component;
import cn.limbo.design_patterns.decorator.ComponentDecorator;
/**
* 滚动条装饰类:具体装饰类
* Created by limbo on 2016/12/6.
*/
public class ScrollBarDecorator extends ComponentDecorator {
public ScrollBarDecorator(Component component) {
super(component);
}
@Override
public void display() {
this.setScrollBar();
super.display();
}
//装饰的方法
//如果希望外部访问到装饰方法,则是半透明的装饰模式,否则为透明的装饰模式,通常建议将装饰方法设置为透明的方式,符合里氏替换原则
private void setScrollBar(){
System.out.println("为构件增加滚动条!");
}
}
DecoratorTest.java
package cn.limbo.test;
import cn.limbo.design_patterns.decorator.Component;
import cn.limbo.design_patterns.decorator.base_component.Window;
import cn.limbo.design_patterns.decorator.decorator_component.BlackBorderDecorator;
import cn.limbo.design_patterns.decorator.decorator_component.ScrollBarDecorator;
import org.junit.Test;
/**
* Created by limbo on 2016/12/6.
*/
public class DecoratorTest {
@Test
public void show(){
Component window , scrollerBarWindow, blackBorderDecorator;//使用抽象构件定义
window = new Window(); //定义基础构件,待装饰的类
scrollerBarWindow = new ScrollBarDecorator(window); //定义装饰后的构件
blackBorderDecorator = new BlackBorderDecorator(window);
// scrollerBarWindow.display();
blackBorderDecorator.display();
}
}
2).半透明模式
3.优缺点
1).装饰模式的主要优点如下:
(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
(2) 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
(3) 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
2).装饰模式的主要缺点如下:
(1) 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
4.适用场景
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类).
5.与桥接模式的区别
6.和适配器模式的关系
装饰模式和适配器模式都是“包装模式(Wrapper Pattern)”,它们都是通过封装其他对象达到设计的目的的,但是它们的形态有很大区别。
理想的装饰模式在对被装饰对象进行功能增强的同时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。而适配器模式则不然,一般而言,适配器模式并不要求对源对象的功能进行增强,但是会改变源对象的接口,以便和目标接口相符合。
装饰模式有透明和半透明两种,这两种的区别就在于装饰角色的接口与抽象构件角色的接口是否完全一致。透明的装饰模式也就是理想的装饰模式,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。相反,如果装饰角色的接口与抽象构件角色接口不一致,也就是说装饰角色的接口比抽象构件角色的接口宽的话,装饰角色实际上已经成了一个适配器角色,这种装饰模式也是可以接受的,称为“半透明”的装饰模式,如下图所示。
在适配器模式里面,适配器类的接口通常会与目标类的接口重叠,但往往并不完全相同。换言之,适配器类的接口会比被装饰的目标类接口宽。
显然,半透明的装饰模式实际上就是处于适配器模式与装饰模式之间的灰色地带。如果将装饰模式与适配器模式合并成为一个“包装模式”的话,那么半透明的装饰模式倒可以成为这种合并后的“包装模式”的代表。