- 装饰模式
装饰模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)
装饰(Decorator)模式中的角色:
- 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
1.1 继承方式
举一个简单的例子,假如现在有一碟炒饭,每个人的口味不一样,有些人喜欢加鸡蛋,有些人喜欢加鸡蛋火腿,有些人喜欢加鸡蛋火腿胡萝卜等,那么就会发现,如果采用继承的方式去实现这个例子,那么没加一个配料,都需要创建新的配料类去继承上一个旧的配料类,那么久而久之,就会产生很多类了,而且还不利于扩展,代码如下:
// 炒饭类
public class FriedRice {
String getDesc() {
return "炒饭";
}
Integer getPrice() {
return 5;
}
}
// 炒饭加鸡蛋类
public class FriedRiceAddEgg extends FriedRice{
String getDesc() {
return super.getDesc() + "+鸡蛋";
}
Integer getPrice() {
return super.getPrice() + 2;
}
}
// 炒饭加鸡蛋加火腿类
public class FriedRiceAddEggAndHam extends FriedRiceAddEgg {
String getDesc() {
return super.getDesc() + "+火腿";
}
Integer getPrice() {
return super.getPrice() + 3;
}
}
// 测试方法
public static void main(String[] args) {
FriedRice friedRice = new FriedRice();
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭5元
FriedRice friedRiceAddEgg = new FriedRiceAddEgg();
System.out.println(friedRiceAddEgg.getDesc() + friedRiceAddEgg.getPrice() + "元"); // 炒饭+鸡蛋7元
FriedRice friedRiceAddEggAndHam = new FriedRiceAddEggAndHam();
System.out.println(friedRiceAddEggAndHam.getDesc() + friedRiceAddEggAndHam.getPrice() + "元");// 炒饭+鸡蛋+火腿10元
}
可以从上面看到,如果我们只需要炒饭加火腿,那么我们还需要创建一个FriedRiceAddHam类去继承FriedRice类,所以继承的方式扩展性非常不好,且需要定义非常多的子类,下面就可以用装饰器模式去改进它
1.2 装饰器模式方式
// 炒饭类
public class FriedRice {
String getDesc() {
return "炒饭";
}
Integer getPrice() {
return 5;
}
}
// 配料表
public abstract class Ingredients extends FriedRice{
private FriedRice friedRice;
public Ingredients(FriedRice friedRice) {
this.friedRice = friedRice;
}
String getDesc() {
return this.friedRice.getDesc();
}
Integer getPrice() {
return this.friedRice.getPrice();
}
}
// 鸡蛋配料
public class Egg extends Ingredients {
public Egg(FriedRice friedRice) {
super(friedRice);
}
String getDesc() {
return super.getDesc() + "+鸡蛋";
}
Integer getPrice() {
return super.getPrice() + 2;
}
}
// 火腿配料
public class Ham extends Ingredients {
public Ham(FriedRice friedRice){
super(friedRice);
}
String getDesc() {
return super.getDesc() + "+火腿";
}
Integer getPrice() {
return super.getPrice() + 3;
}
}
// 测试方法
public static void main(String[] args) {
FriedRice friedRice = new FriedRice();
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭5元
friedRice = new Egg(friedRice);
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭+鸡蛋7元
friedRice = new Egg(friedRice);
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋9元
friedRice = new Ham(friedRice);
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋+火腿12元
}
可以看到,使用装饰器模式的方法实现,与普通的继承方法实现,最大的区别就是一种配料只有一个类,而且在加配料的时候,也可以直接想加多少就加多少,不需要说一个鸡蛋一个类,两个鸡蛋也要创建一个类,这样可以带来比继承更加灵活的扩展功能,使用也更加方便。
1.3 总结
装饰器模式与代理模式对比:
- 装饰器模式就是一种特殊的代理模式。
- 装饰器模式强调自身的功能扩展,用自己说了算的透明扩展,可动态定制的扩展;代理模式强调代理过程的控制。
- 获取目标对象构建的地方不同,装饰者是从外界传递进来的,可以通过构造方法传递;静态代理是在代理类内部创建,以此来隐藏目标对象。
适用场景:
- 用于扩展一个类的功能或者给一个类添加附加职责。
- 动态的给一个对象添加功能,这些功能同样也可以再动态的撤销。
优点:
- 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
- 通过使用不同装饰类以及这些装饰类的排列组合,可实现不同效果。
- 装饰器完全遵守开闭原则。
缺点:
- 会出现更多的代码,更多的类,增加程序的复杂性。
- 动态装饰时,多层装饰会更复杂。