定义
装饰器模式也称为包装模式,是指在不改变原有对象的基础上,将功能附加到原有对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。
举例
这里举例一个日常生活常见的例子。我们每天吃早饭可能会买的煎饼果子。煎饼果子是可以加上调料和其他肉食的。如果觉得不够吃,可以加上鸡蛋或者香肠。
- 创建煎饼果子类。提供getName和getPrice方法。
public class BatterCake {
protected String getName(){
return "煎饼";
}
protected int getPrice(){
return 5;
}
}
- 觉得不够吃,想加一个鸡蛋。创建一个类BattarCakeWithOneEgg继承BatterCake类,重写getName和getPrice方法。
public class BattarCakeWithOneEgg extends BatterCake {
@Override
protected String getName() {
return super.getName() + "加一个鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice() + 1;
}
}
- 还是觉得不够吃,想再加一个香肠。创建一个类继续继承BattarCakeWithOneEgg,重写getName和getPrice方法。
public class BatterCakeWithOneEggOneSausage extends BattarCakeWithOneEgg {
@Override
protected String getName() {
return super.getName() + "加一个香肠";
}
@Override
protected int getPrice() {
return super.getPrice() + 2;
}
}
- 测试代码。
public class Test {
public static void main(String[] args) {
//想吃煎饼果子
BatterCake batterCake = new BatterCake();
System.out.println(batterCake.getName() + ",价格:" + batterCake.getPrice());
//想再加一个鸡蛋
BattarCakeWithOneEgg battarCakeWithOneEgg = new BattarCakeWithOneEgg();
System.out.println(battarCakeWithOneEgg.getName() + ",价格:" + battarCakeWithOneEgg.getPrice());
//想再加一个鸡蛋一个香肠
BatterCakeWithOneEggOneSausage batterCakeWithOneEggOneSausage = new BatterCakeWithOneEggOneSausage();
System.out.println(batterCakeWithOneEggOneSausage.getName() + ",价格:" + batterCakeWithOneEggOneSausage.getPrice());
}
}
- 看看结果。
这个时候,如果我想再一个鸡蛋。就需要继续继承BatterCakeWithOneEggOneSausage 这个类。想再加,就必须再继承。
这样不停的定制,显然是不科学的做法。
下面就用装饰器模式来改造下这个代码。
- 创建一个抽象的煎饼对象,提供getName和getPrice方法。
public abstract class BatterCake {
public abstract String getName();
public abstract int getPrice();
}
- 创建一个基础的煎饼果子类(基础套餐)
public class BaseBatterCake extends BatterCake {
@Override
public String getName() {
return "煎饼";
}
@Override
public int getPrice() {
return 5;
}
}
- 创建一个煎饼果子基础套餐的装饰器,将抽象的BatterCake类以静态代理的方式注入到BatterCakeDecorator 的构造方法中,然后调用它的getName和getPrice的方法。
public class BatterCakeDecorator extends BatterCake {
private BatterCake batterCake;
public BatterCakeDecorator(BatterCake batterCake) {
this.batterCake = batterCake;
}
@Override
public String getName() {
return batterCake.getName();
}
@Override
public int getPrice() {
return batterCake.getPrice();
}
}
- 创建一个加了鸡蛋的煎饼果子套餐,继承BatterCake,在构造方法中注入BatterCake,然后重写getName和getPrice的方法。
public class BatterCakeEggDecorator extends BatterCakeDecorator {
public BatterCakeEggDecorator(BatterCake batterCake) {
super(batterCake);
}
@Override
public String getName() {
return super.getName() + "加一个鸡蛋";
}
@Override
public int getPrice() {
return super.getPrice() + 1;
}
}
- 创建一个加香肠的煎饼果子套餐,同样继承BatterCake,在构造方法中注入BatterCake,然后重写getName和getPrice的方法。
public class BatterCakeSausageDecorator extends BatterCakeDecorator {
public BatterCakeSausageDecorator(BatterCake batterCake) {
super(batterCake);
}
@Override
public String getName() {
return super.getName() + "加一个香肠";
}
@Override
public int getPrice() {
return super.getPrice() + 2;
}
}
- 测试。
public class Test {
public static void main(String[] args) {
BatterCake batterCake;
//点一个基础套餐
batterCake = new BaseBatterCake();
//加一个鸡蛋
batterCake = new BatterCakeEggDecorator(batterCake);
//加一个香肠
batterCake = new BatterCakeSausageDecorator(batterCake);
//再加一个香肠
batterCake = new BatterCakeSausageDecorator(batterCake);
System.out.println("买了" + batterCake.getName() + ",一共花了" + batterCake.getPrice() + "元钱");
}
}
-
查看结果。
-
看一下类图。
这里的BatterCake就是一个抽象的组件,它充当了被装饰的原始对象,规定了被装饰对象的行为。BaseBatterCake就是具体组件,他是实现或者继承抽象组件的一个具体对象,也就是被装饰对象。BatterCakeDecorator就是通用的装饰器,它的内部必然有一个属性指向抽象的组件。它的实现一般是一个抽象类,主要是为了让它的子类按照其构造形式传入一个抽象的组件,这是强制的通用行为。而如果系统中的装饰逻辑比较的单一,并不需要许多的装饰器,那么就可以直接省略这个类,直接实现一个具体的装饰器就可以。而下面的 BatterCakeEggDecorator 和 BatterCakeSausageDecorator 就是具体的装饰器对象。理论上,每一个对象都扩展了BaseBatterCake的功能。
总结
装饰器模式角色分配符合设计模式的里氏替换原则,依赖倒置原则,从而使得它具备很强的扩展性,最终满足开闭原则。
- 装饰器模式和代理模式的对比
装饰器更强调对被装饰对象自身的扩展,而代理模式更强调代理对象的功能增强。
- 代理模式的优点
(1).是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
(2).可以通过构造不同的装饰类,对这些类进行排列组合,实现不同的效果。
(3).装饰器模式完全遵循开闭原则。
- 代理模式的缺点
(1).会出现很多的代码,出现更多的类,增加了系统的复杂度。
(2).动态装饰时,多层装饰会比较复杂。