装饰器模式
定义
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装(节选自Runoob.com)。我们可以尝试从字面上去理解,通过设计一个装饰类对原有类进行梳妆打扮,使其更加“漂亮”,初从定义了解装饰器模式,让我买下了一个疑问,怎么感觉跟代理模式这么像呢?接下来,我们就带着疑问来学习装饰器模式。
UML类图
从类图上可以看到,装饰器模式包括一下几个组成部分:
- Component:抽象组件,定义了一组抽象的接口,规定这个被装饰类有哪些功能。
- ConcreteComponent: 实现这个抽象组件的所有功能。
- Decorator:装饰器角色, 它持有一个Component 对象实例的引用。
- ConcreteDecorator:具体的装饰器实现者,负责实现装饰器角色定义的功能。
示例
我们还是以租房为例来描述装饰器模式:
抽象组件类(House):
public interface House {
public int Rent();
public String getDescription();
}
抽象组件的实现类(Apartment):
/**
* @author jhz
* @date 18-8-16 下午10:04
*/
public class Apartment implements House {
@Override
public int Rent() {
return 10000;
}
@Override
public String getDescription() {
return "I'm living an apartment";
}
}
抽象装饰器类(AbstractDecorator):这里定义成抽象类,是因为它并没有做具体的装饰操作,我们不应该去实例化它
/**
* @author jhz
* @date 18-8-16 下午10:14
*/
public abstract class AbstractDecorator implements House{
protected House decorator;
public AbstractDecorator(House decorator){
this.decorator = decorator;
}
@Override
public int Rent() {
return decorator.Rent();
}
@Override
public String getDescription() {
return decorator.getDescription();
}
}
装饰器类的实现(MahoganyDecorator):红木地板装饰类
/**
* @author jhz
* @date 18-8-16 下午10:36
*/
public class MahoganyDecorator extends AbstractDecorator {
public MahoganyDecorator(House decorator){
super(decorator);
}
@Override
public int Rent() {
return super.Rent() + 1000;
}
@Override
public String getDescription() {
return super.getDescription() + "with mahogany floor!";
}
}
装饰器类的实现(CurtainDecorator):窗帘装饰类
/**
* @author jhz
* @date 18-8-16 下午10:45
*/
public class CurtainDecorator extends AbstractDecorator {
public CurtainDecorator(House decorator){
super(decorator);
}
@Override
public int Rent() {
return super.Rent() + 500;
}
@Override
public String getDescription() {
return super.getDescription() + "with beautiful curtain!";
}
}
测试类:
/**
* @author jhz
* @date 18-8-16 下午10:46
*/
public class RentRoom {
public static void main(String[] args) {
//租一个公寓
House house = new Apartment();
System.out.println(house.Rent()+":"+house.getDescription());
//为公寓铺上红木地板
house = new MahoganyDecorator(house);
System.out.println(house.Rent()+":"+house.getDescription());
//为公寓装上窗帘
house = new CurtainDecorator(house);
System.out.println(house.Rent()+":"+house.getDescription());
}
}
结果:
特点:
优点:可以看到,我们通过装饰器模式完成了对一个房屋的装修,每次我们调用具体的装饰类,我们都为房子增添了一些装饰。适用于对类的功能进行扩展的场景,通过装饰器类,可以动态的对类进行装饰,同样也能撤销装饰(撤销装饰同样也是一种装饰器)。这种模式可以使装饰类与被装饰类解耦,独立扩展(公寓本身与装饰并不会耦合,可以把公寓的装饰都换掉,也能把装饰都移到另一个公寓中),可以看到在实现租房以及装饰的过程中,所有的装饰任务都是交给house对象完成的(其实本质上是由三个对象完成的),可以理解为动态的将装饰责任附加到house对象上。
缺点:由于使用装饰器模式,可以比使用继承关系需要较少数目的类(在装饰器类中保留了被装饰类的功能,比如CurtainDecorator中通过继承抽象装饰器类可以直接获得房屋的租金,这本不应该是它需要了解的)。使用较少的类,当然使设计比较易于进行。但是另一方面,由于使用装饰器模式会产生比使用继承关系更多的对象,更多的对象会使得查错变得困难,特别是这些对象看上去都很像。
与代理模式对比
代理模式和装饰模式非常类似,甚至代码都类似。二者最主要的区别是:代理模式中,代理类对被代理的对象有控制权,决定其执行或者不执行。而装饰模式中,装饰类对代理对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能,仅此而已,这也是它们从名字上就表现出来的区别,结合之前代理模式的介绍可以看到,代理类(中介)直接决定你能不能租到房子,而装饰器类(各种装修商)并不能决定你是否能租到房子,他们起到的作用是为你装饰,而不能影响你租房。