装饰器模式(Decorator Pattern):动态地给对象添加一些额外的职责,就增加功能来说,它比生成子类更为灵活。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
类图及角色
抽象被装饰角色(component):装饰器模式中公共方法的类,在结构图的顶层。
具体被装饰角色(concrete component):具体被装饰的类。
装饰者角色(decorator):装饰器模式中的核心对象,所有具体装饰器对象的父类,完成装饰器的部分职能。它有个鲜明的特点:继承至component,同时包含一个component成员变量。装饰器模式动机中的动态增加功能就是在这里实现的。
具体装饰对象角色(concrete decorator):继承装饰者,完成具体的装饰功能。装饰功能的实现是通过调用被装饰对象对应的方法,加上装饰对象自身的方法。
装饰器模式的优点和缺点
优点:
1、装饰类和被装饰类可以独立发展,不会相互耦合。
2、装饰模式是继承的一个替代模式,它们的目的都是扩展对象的功能。
3、装饰模式可以动态扩展一个实现类的功能,具有更大的灵活性,动态地决定粘上还是去掉一个装饰;使用继承扩展,这些功能在编译时就确定了,功能是可预见的,是静态的。
缺点:
多层装饰比较复杂。虽然比继承关系使用更少的类,但是使用了更多的对象(更多的对象使查错变得困难,特别使它们还很像的时候)
透明装饰器和不透明装饰器
透明装饰器:整个Decorator的结构中所有的类保持同样的“接口”,这里是共同方法的意思。
不透明装饰器:现实中绝大多数都是不透明的,它们的“接口”在某些子类中得到增强。区分透明和不透明主要看这个类和顶层类或接口是否有同样的公共方法。
//装饰器模式中所有类的父类
public abstract class Ingredient {
public abstract String getDescription();
public abstract double getCost();
public void printDescription() {
System.out.println("Name" + this.getDescription());
System.out.println("RMB" + this.getCost());
}
}
//具体被装饰类
public class Bread extends Ingredient {
private String description;
public Bread(String description) {
this.description = description;
}
@Override
public String getDescription() {
return description;
}
@Override
public double getCost() {
return 3;
}
}
//装饰者
public abstract class Decorator extends Ingredient {
public Ingredient ingredient;
public Decorator(Ingredient ingredient) {
this.ingredient = ingredient;
}
@Override
public abstract String getDescription();
@Override
public abstract double getCost();
}
//具体装饰类
public class Gerely extends Decorator {
public Gerely(Ingredient ingredient) {
super(ingredient);
}
@Override
public String getDescription() {
String base = ingredient.getDescription();
return base + "\n" + "芹菜";
}
@Override
public double getCost() {
double basePrice = ingredient.getCost();
return basePrice + 6;
}
}
//具体装饰类
public class Pork extends Decorator {
public Pork(Ingredient ingredient) {
super(ingredient);
}
@Override
public String getDescription() {
String base = ingredient.getDescription();
return base + "\n" + "pork";
}
@Override
public double getCost() {
double basePrice = ingredient.getCost();
return basePrice + 6;
}
}