装饰模式的目的在于将一些不同的功能块按正确的顺序串联起来进行控制,并且动态的增加我们想要的功能来对主体进行装饰。
比如我们穿衣服,如果把每件衣服看成一个装饰功能,那么我们根据衣服的种类不同可以有很多种搭配方式和穿衣顺序,用程序来体现的话,为了易于扩展,我们把每个衣服都写成一个类,都有一个方法show()实现穿衣的逻辑,并且可以提取出公共的抽象,在客户端方法中,我们new出所需要穿的衣服的对象,按照我们想要的顺序依次去调用show()方法,这样是可以实现我们的需求,但是由客户端代码依次去调用show()方法,还要关心我是按照什么顺序把这些衣服穿起来的,总感觉这个穿衣的服务接口封装的不够完整,同时这么做也不太不符合前面提到的单一职责原则。
装饰模式正好解决了这样的问题, 在客户端我们只需要定义好衣服的种类和穿衣的顺序,剩下的都交给服务端的代码完成。
先创建一个Person类作为主体。
public class Person {
public Person(){ //注意如果重载了构造方法需要手动把默认的无参构造方法写出来
};
private String name;
public Person(String name){
this.name = name;
}
public void show(){
System.out.println("装扮的"+name);
}
}
装饰的抽象父类,继承Person类。
public abstract class Finery extends Person{
//理解成需要装饰的对象
protected Person component;
public void decorate(Person component){
this.component = component;
}
public void show(){
component.show();
}
}
接着添加几个具体的衣服类
public class BigTrouser extends Finery{
//垮裤的穿着实现
public void show(){
System.out.print("垮裤 ");
super.show();
}
}
public class Sneaker extends Finery{
//牛仔裤的穿着实现
public void show(){
System.out.print("牛仔裤 ");
super.show();
}
}
public class TShirt extends Finery{
//T恤的穿着实现
public void show(){
System.out.print("T恤 ");
super.show();
}
}
客户端代码。
public class Main {
public static void main(String[] ar){
Person person = new Person("小明");
//定义需要穿哪几件衣服
BigTrouser bigTrouser = new BigTrouser();
Sneaker sneaker = new Sneaker();
TShirt tShirt = new TShirt();
//定义穿衣的顺序,注意是倒序的
bigTrouser.decorate(person); //垮裤去装饰初始的人
sneaker.decorate(bigTrouser); //牛仔裤去装饰穿了垮裤的人
tShirt.decorate(sneaker); //T恤去装饰穿了垮裤和牛仔裤的人
//调用穿衣方法
tShirt.show();
}
}
运行结果。
T恤 牛仔裤 垮裤 装扮的小明
分析一下定义穿衣顺序的那一段代码,bigTrouser中继承了父类的decorate()方法,bigTrouser.decorate(person)将初始的人的对象赋给了component 对象,即定义了bigTrouser将要装饰的对象,同理,sneaker装饰了这个即将要被bigTrouser装饰的人,tShirt装饰了即将要被bigTrouser和sneaker装饰的人。
通过这个例子,我们了解到,装饰模式其实是为已有功能动态地添加新的功能的一种方式,它把每个要装饰的功能放在单独类中,并让这个类去装饰它所要装饰的对象,因此,当需要执行特殊行为时,客户端代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。