定义
向现有的对象添加新的功能,并不改变其结构
演化
1.饮品是生活中最常见的物质,它是一个种类庞大的家族。先定义一个抽象的饮品类,它只有一个属性weight来表示重量。
public abstract class Drink {
private int weight;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
2.之后就可以通过继承自Drink来定义一些实际的饮品类。如Water,Sugar,Honey
public class Water extends Drink {
}
public class Sugar extends Drink {
}
3.使用继承的办法,逐步建立起了饮品家族。当有n个饮品种类时,需要继承自Drink创建n个实际饮品类。此时这个继承关系只有一层,比较清晰。现在复杂化场景,请给出一杯糖水:我们会发现这个简单的SugarWater几乎没有办法定义了,Java是不支持多继承的,它没有办法同时继承自Water,Sugar类从而获得这二者的属性。那只能另辟蹊径,让SugarWater先继承自Water,再给它补充Sugar的属性才行。
public class SugarWater extends Water {
private Sugar sugar;
public SugarWater(Sugar sugar) {
this.sugar = sugar;
}
@Override
public int getWeight() {
return super.getWeight() + sugar.getWeight();
}
}
这时就产生了第二层的继承结构。这第二层可以组合方式将会有Cn2种。当三种饮品组合在一起的时候,将会出现第三层继承结构,组合方式有Cn3种…按照这种写法下去,Drink的产品族数量直接爆炸了。当形成继承链之后,较底层的类的变化也势必会影响较上层的类。
4.由3可知使用继承的办法对于多层次复杂的场景是难以适应的,此时就需要用到装饰模式了。直接定义一个装饰类DrinkWrapper。它继承自Drink,但同时又能持有一个Drink类型的参数。也就是说,它拥有了和一个Drink对象互相组合的能力,从而避开了对继承的使用。并且由于隔开了和实际对象的直接访问,还可以再额外附加一些修饰性的功能。
public class DrinkWrapper extends Drink {
private Drink drink;
public DrinkWrapper(Drink drink) {
this.drink = drink;
}
@Override
public int getWeight() {
return super.getWeight() + drink.getWeight();
}
}
在这种情况下,创建一个糖水对象的过程就交给了调用方去处理
public static void display() {
Drink waterWrapper = new DrinkWrapper(new Water());
Drink sugarWater = new DrinkWrapper(waterWrapper);
}
装饰模式的使用就解决了继承结构冗余的问题,它只需要定义一些实际类,它们都只继承自一个统一抽象类或实现一个接口。后续对于这些组合都通过逐步包装的方式实现,而不再需要继承的方法。对于n个产品,只需要n个实现类及一个包装类即可。
优点:
(1)避免使用继承方式扩展带来的灵活性差,无限扩展问题
(2)可以动态的扩展一个对象的功能,还可以选择不同的装饰器,从而实现不同的行为
(3)组件类与装饰类可以独立变化,用户可以自由组合组件类与装饰类来获取不同的功能
缺点:
(1)如果基类被改变,则更容易影响全局
(2)结构过深,容易出错,且不容易排查
(3)装饰层数不能过多,否则影响效率
使用场景:
(1)不影响其他对象情况下,动态透明地给它添加功能
(2)需要动态给对象增加功能,这些功能还支持撤销
(3)不能使用继承方式扩展,或者继承不利于系统扩展维护