学习经典的设计模式的时候装饰者模式真是让人头疼,但是掌握了其基本思想后并不难实现。
首先“装饰者”已经告诉了我们两个基本信息
- 要有东西被装饰,可以看作一个核心
- 要有装饰的东西,可以看作不同的属性或功能
很简单吧,就两个信息足以让我们写出全部的模板代码。装饰者模式要做的,就是把不同的功能一层一层地包到核心的外面而已。
首先设计模式看多了就知道,“核心类”和“装饰类”必然是分为抽象的和具体的类。在此次例子中,咖啡就是我们的核心,需要添加的配料就是我们的装饰。
但是首先来看最终的客户端调用代码:
public static void main(String[] args) {
//下单一杯咖啡Espresso(意大利咖啡)---核心
Order order = new Espresso();
//加一点牛奶 ---装饰
order = new Milk(order);
//加一点巧克力 ---装饰
order = new Chocolate(order);
System.out.println("一共"+order.cost()+"元:" + order.getDsc());
}
打印结果:
怎么样,是不是很符和我们的预料,那么接下来就来看看如何实现它!
从客户端代码可知,这个Order类从头用到尾,既接收了核心实现类Espresso又接收了装饰者实现类Milk和Chocolate。
那么可以获取结论一,抽象核心类和抽象装饰类都是这个Order类的子类!
既然Order类是最顶层的父类,那么它的属性必然是其子类都具有的,这里我们就将这些属性确认为价格和描述/名称(咖啡和配料都有单独的价格和名称)
给出Order类的代码:
public abstract class Order {
public String dsc;//描述
private long price = 0;//价格
public String getDsc() {
return dsc;
}
public void setDsc(String dsc) {
this.dsc = dsc;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
public abstract long cost();
}
可以发现它除了两个属性的getter和setter方法外,留了个cost方法(获取此次订单总价)给子类实现。
接下来就可以把核心类Espresso类写出来了,当然咖啡不只有一种,所以我们为了扩展性,别怕麻烦给Espresso也确定一个父类,即Coffee咖啡类
Coffee类代码:
public class Coffee extends Order {
//获取订单价格
@Override
public long cost() {
return super.getPrice();
}
}
可以发现Coffee类没干什么事,只是返回Order订单类的价格而已。
Espresso的代码:
public class Espresso extends Coffee{
public Espresso(){
setDsc("意大利咖啡");
setPrice(10);
}
}
可知这个类调用了父类的方法,给父类两个基本属性赋了值,先来看看继承链。
再来回顾客户端第一行代码
public static void main(String[] args) {
//核心咖啡Espresso(意大利咖啡)
Order order = new Espresso();
//装饰者牛奶
order = new Milk(order);
//装饰者巧克力
order = new Chocolate(order);
System.out.println("一共"+order.cost()+"元:" + order.getDsc());
}
就可以发现此时order的dsc和price属性已经分别被赋上了“意大利咖啡”和“10”。
再来看一看第二行代码,装饰类的构造参数是核心类,这点很好理解。要装饰一个对象那我先得持有这个对象吧。
而由结论一可知,核心类装饰类统统是继承自Order类,所以抽象装饰类的构造参数就是它的父类Order类,这就是结论二
至此,核心咖啡我们已经有了,剩下的就是添加配料了,同上,我们也分别写出装饰抽象类和装饰实现类Milk,Chocolate。
装饰抽象类代码:
public abstract class Decorator extends Order {
private Order drink;
public Decorator(Order drink){
this.drink = drink;
}
@Override
public long cost() {
return super.getPrice() + drink.cost();
}
//调味的描述+咖啡的描述
@Override
public String getDsc(){
return super.getDsc() + "/" + drink.getDsc();
}
}
这里覆盖了cost方法,将此次订单的价格变成了装饰者配料的价格 + 核心类咖啡的价格。也覆盖了订单的名称,在原来的描述上加上了装饰者的名称。
由客户端代码和结论一可知这里的super.getXXX获取的是装饰者实现类的属性
装饰者实现类的代码:
public class Milk extends Decorator {
public Milk(Order obj) {
super(obj);
setDsc("牛奶");
setPrice(2);
}
}
public class Chocolate extends Decorator {
public Chocolate(Order obj) {
super(obj);
setDsc("巧克力");
setPrice(1);
}
}
装饰者实现类的继承链是这样的
和核心类的继承链是不是非常类似?最后我们多加几个咖啡子类和配料子类,整个关系图就变得非常对称了!
记住最重要的结论
- 装饰者和被装饰者都继承自一个父类
- 装饰者类里面聚合了被装饰者
- 装饰者类需要重写父类方法,基于装饰者基础功能上去实现业务逻辑。这样每包一层装饰者,功能就会得到对应的提升
再再来看一遍客户端代码,是不是感觉装饰者模式既简单又精妙呢?!
public static void main(String[] args) {
//核心咖啡Espresso(意大利咖啡)
Order order = new Espresso();
//装饰者牛奶
order = new Milk(order);
//装饰者巧克力
order = new Chocolate(order);
System.out.println("一共"+order.cost()+"元:" + order.getDsc());
}
Java的IO包下的代码就是装饰者模式的极致运用
举个例子:
- InputStream是抽象类Order
- FileInputStream和其他是核心子类
- FilterInputStream是Decorator,其中包含有被装饰者
- DataInputStream是Decorator子类
具体实现可以自己慢慢去品味~