装饰设计模式
装饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,装饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。
装饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
适用性
1.当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。
2.当某个对象的职责经常发生变化或者经常需要动态的增加职责,避免为了适应这样的变化,而增加继承子类扩展的方式,因为这种方式会造成子类膨胀的速度过快,难以控制。
UML
图片来自:https://zh.wikipedia.org/wiki/%E4%BF%AE%E9%A5%B0%E6%A8%A1%E5%BC%8F#/media/File:Decorator_UML_class_diagram.svg
例子
一个卖饮料的接口Drinks,里面包含计算该饮料价格的抽象方法:
public interface Drinks {
int calculate();
}
然后是4个具体的饮料,实现Drinks接口,并实现各自的calculate方法:
public class ConcreteDrink1 implements Drinks {
public int calculate() {
int price = 3;
System.out.println("饮品1\t" + "价格:" + price);
return price;
}
}
public class ConcreteDrink2 implements Drinks {
public int calculate() {
int price = 4;
System.out.println("饮品2\t" + "价格:" + price);
return price;
}
}
public class ConcreteDrink3 implements Drinks {
public int calculate() {
int price = 1;
System.out.println("饮品3\t" + "价格:" + price);
return price;
}
}
public class ConcreteDrink4 implements Drinks {
public int calculate() {
int price = 6;
System.out.println("饮品4\t" + "价格:" + price);
return price;
}
}
现在我需要买饮料并且需要不同的口味,那么就需要一个负责既能买饮料又能根据不同口味实现不同口味的饮料的装饰类
DecorDressing:
public class DecorDressing {
private Drinks drinks;
public DecorDressing(Drinks drinks) {
super();
this.drinks = drinks;
}
public int calculate() {
return drinks.calculate() + drinks.calculate();
}
}
该类就是负责装饰饮料Drinks对象的。
通过使用装饰模式,可以在运行时扩充一个类的功能。
原理是:增加一个装饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。
下面是6个具体的饮料的口味:
public class ConcreteDressing1 extends DecorDressing {
public ConcreteDressing1(Drinks drinks) {
super(drinks);
}
@Override
public int calculate() {
int price = 3;
System.out.println("调料1\t" + "价格:" + price);
return price;
}
}
public class ConcreteDressing2 extends DecorDressing {
public ConcreteDressing2(Drinks drinks) {
super(drinks);
}
@Override
public int calculate() {
int price = 2;
System.out.println("调料2\t" + "价格:" + price);
return price;
}
}
public class ConcreteDressing3 extends DecorDressing {
public ConcreteDressing3(Drinks drinks) {
super(drinks);
}
@Override
public int calculate() {
int price = 3;
System.out.println("调料3\t" + "价格:" + price);
return price;
}
}
public class ConcreteDressing4 extends DecorDressing {
public ConcreteDressing4(Drinks drinks) {
super(drinks);
}
@Override
public int calculate() {
int price = 4;
System.out.println("调料4\t" + "价格:" + price);
return price;
}
}
public class ConcreteDressing5 extends DecorDressing {
public ConcreteDressing5(Drinks drinks) {
super(drinks);
}
@Override
public int calculate() {
int price = 5;
System.out.println("调料5\t" + "价格:" + price);
return price;
}
}
public class ConcreteDressing6 extends DecorDressing {
public ConcreteDressing6(Drinks drinks) {
super(drinks);
}
@Override
public int calculate() {
int price = 6;
System.out.println("调料6\t" + "价格:" + price);
return price;
}
}
然后是测试客户端:
public class Client {
public static void main(String[] args) {
Drinks drinks = new ConcreteDrink1();
DecorDressing dressing1 = new ConcreteDressing1(drinks);
DecorDressing dressing3 = new ConcreteDressing3(drinks);
DecorDressing dressing6 = new ConcreteDressing6(drinks);
int total = drinks.calculate() + dressing1.calculate() +
dressing3.calculate() + dressing6.calculate();
System.out.println("总价格:" + total);
}
}
结果:
总结
装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
装饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。