原理概述
装饰者模式(decorator),顾名思义,就是为方便动态地给对象进行装饰所设计的模式。
这个模式要解决的问题就是:当存在一些装饰品类以及被装饰品类时,要使用装饰品装饰一个对象,这些装饰品允许重复且不限制先后。
那么应该怎么表示这个装饰的过程呢?一般来说,装饰的体现在于原对象的属性会有所增益像是体积、价格、评分等,例如将人装饰以衣服,将订餐装饰以作料等。从我个人角度看,基于这样的特性,装饰物作用不像是一个组成物,而更像是一个滤镜。也就是说,它们不直接影响被装饰物而是在程序与被装饰物的交互过程中体现影响。
那么说到滤镜,它应在怎么样加到物品上呢,其实就直接一层一层叠起来就好了。像这样:
从我的角度看,放在程序中,想实现这样的方便添加又层次清晰的数据结构当选链表了。因此可以使用链表来实现这个装饰的逻辑,链表的头是最后一层装饰,链表尾是物品,每个节点的后继作为一个整体被该节点修饰。
对于任何的属性访问或者是装饰者起作用的方法,都可从最后一层装饰开始,递归解决。且这样的设计便于添加新的装饰,可扩展性强;使用简单不需要考虑具体的装饰步骤,封装性好。
具体实现
想要实现链表结构,就需要有后继节点指针(引用),这就遇到了一个问题:装饰物与被装饰物的兼容性。也就是说如何让装饰物后面既可以连接装饰物,也可以连接具体的物品。使用两个引用?这显然不够优化
那有没有更好的解决方案呢?对了,使用多态呀!我们让物品和装饰物共同继承同一个父类,在装饰物中组合一个父类的对象就好了。画成类图就是这个样子:
举个例子
光是理论说明,还不足以体现装饰者模式的精妙之处,下面我们来举个具体的粟子。
我们假设这样一个场景:制作衣服,衣服胚子有T恤和衬衫(整两个就够了)。衣服的装饰品有:扣子,蝴蝶结,花边(博主编不出来了)。衣服需要计算最终的价格以及对衣服的描述。
我们来简单的构建这几个类:
首先是共同的父类:
public abstract class Clothes {
protected String describtion;
protected double price;
public abstract String desc(); //获得描述
public abstract double cost(); //计算价格
}
接着是两个衣服胚子类:
/*T恤衫*/
public class TShirt extends Clothes{
public TShirt() {
super.describtion = "T恤衫";
super.price = 20.0;
}
public String desc() {
return "胚子:" + super.describtion;
}
public double cost() {
return super.price;
}
}
/*衬衫*/
public class Shirt extends Clothes{
public Shirt() {
super.describtion = "衬衫";
super.price = 30.0;
}
public String desc() {
return "胚子:" + super.describtion;
}
public double cost() {
return super.price;
}
}
接着是装饰者父类:
public abstract class Decorator extends Clothes {
/*被装饰者*/
protected Clothes clothes;
/*设定装饰者*/
public Decorator(Clothes clothes) {
this.clothes = clothes;
}
/*递归计算价格*/
public double cost() {
return this.getPrice() + clothes.cost();
}
/*递归生成描述*/
public String desc() {
return "装饰品:" + this.getDescribtion() + " " + clothes.desc();
}
}
三个装饰类:
/*扣子*/
public class Button extends Decorator {
public Button(Clothes clothes) {
super(clothes);
super.describtion = "扣子";
super.price = 3.0;
}
}
/*蝴蝶结*/
public class Bow extends Decorator {
public Bow(Clothes clothes) {
super(clothes);
super.describtion = "蝴蝶结";
super.price = 10.0;
}
}
/*花边*/
public class Lace extends Decorator{
public Lace(Clothes clothes) {
super(clothes);
super.describtion = "花边";
super.price = 15.0;
}
}
场景类:
/*三枚扣子和花边的衬衫*/
@Test
public void Shirt() {
Clothes clothes = new Shirt();
for(int i = 0;i < 3;i++) {
clothes = new Button(clothes);
}
clothes = new Lace(clothes);
System.out.println("三枚扣子和花边的衬衫的价格是:" + clothes.cost());
System.out.println(clothes.desc());
}
/*两枚扣子和蝴蝶结的T恤*/
@Test
public void TShirt() {
Clothes clothes = new TShirt();
for(int i = 0;i < 2;i++) {
clothes = new Button(clothes);
}
clothes = new Bow(clothes);
System.out.println("两枚扣子和蝴蝶结的T恤的价格是:" + clothes.cost());
System.out.println(clothes.desc());
}
结果如下:
可以看到,这样的设计保证了程序的可扩展性,当需要新添加衣服胚子或者装饰品时仅需要额外添加类即可,这也满足了开闭原则。
当然,这只是最简单的装饰者模式的实现,该版本仅支持装饰品的添加,至于其他的删改查功能,大家可以借鉴链表结构对于该功能的实现方式进行实现,本文就到这里。
设计模式其他文章: