【设计模式】装饰器模式

定义

装饰器模式也称为包装模式,是指在不改变原有对象的基础上,将功能附加到原有对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

举例

这里举例一个日常生活常见的例子。我们每天吃早饭可能会买的煎饼果子。煎饼果子是可以加上调料和其他肉食的。如果觉得不够吃,可以加上鸡蛋或者香肠。

  1. 创建煎饼果子类。提供getName和getPrice方法。
1
2
3
4
5
6
7
8
9
10
public class BatterCake {

protected String getName(){
return "煎饼";
}

protected int getPrice(){
return 5;
}
}
  1. 觉得不够吃,想加一个鸡蛋。创建一个类BattarCakeWithOneEgg继承BatterCake类,重写getName和getPrice方法。
1
2
3
4
5
6
7
8
9
10
11
12
public class BattarCakeWithOneEgg extends BatterCake {

@Override
protected String getName() {
return super.getName() + "加一个鸡蛋";
}

@Override
protected int getPrice() {
return super.getPrice() + 1;
}
}
  1. 还是觉得不够吃,想再加一个香肠。创建一个类继续继承BattarCakeWithOneEgg,重写getName和getPrice方法。
1
2
3
4
5
6
7
8
9
10
11
12
public class BatterCakeWithOneEggOneSausage extends BattarCakeWithOneEgg {

@Override
protected String getName() {
return super.getName() + "加一个香肠";
}

@Override
protected int getPrice() {
return super.getPrice() + 2;
}
}
  1. 测试代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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());
}
}
  1. 看看结果。

这个时候,如果我想再一个鸡蛋。就需要继续继承BatterCakeWithOneEggOneSausage 这个类。想再加,就必须再继承。

这样不停的定制,显然是不科学的做法。

下面就用装饰器模式来改造下这个代码。

  1. 创建一个抽象的煎饼对象,提供getName和getPrice方法。
1
2
3
4
public abstract class BatterCake {
public abstract String getName();
public abstract int getPrice();
}
  1. 创建一个基础的煎饼果子类(基础套餐)
1
2
3
4
5
6
7
8
9
10
11
public class BaseBatterCake extends BatterCake {
@Override
public String getName() {
return "煎饼";
}

@Override
public int getPrice() {
return 5;
}
}
  1. 创建一个煎饼果子基础套餐的装饰器,将抽象的BatterCake类以静态代理的方式注入到BatterCakeDecorator 的构造方法中,然后调用它的getName和getPrice的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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();
}
}
  1. 创建一个加了鸡蛋的煎饼果子套餐,继承BatterCake,在构造方法中注入BatterCake,然后重写getName和getPrice的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
}
  1. 创建一个加香肠的煎饼果子套餐,同样继承BatterCake,在构造方法中注入BatterCake,然后重写getName和getPrice的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}
}
  1. 测试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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() + "元钱");
}
}
  1. 查看结果。

  2. 看一下类图。

这里的BatterCake就是一个抽象的组件,它充当了被装饰的原始对象,规定了被装饰对象的行为。BaseBatterCake就是具体组件,他是实现或者继承抽象组件的一个具体对象,也就是被装饰对象。BatterCakeDecorator就是通用的装饰器,它的内部必然有一个属性指向抽象的组件。它的实现一般是一个抽象类,主要是为了让它的子类按照其构造形式传入一个抽象的组件,这是强制的通用行为。而如果系统中的装饰逻辑比较的单一,并不需要许多的装饰器,那么就可以直接省略这个类,直接实现一个具体的装饰器就可以。而下面的 BatterCakeEggDecorator 和 BatterCakeSausageDecorator 就是具体的装饰器对象。理论上,每一个对象都扩展了BaseBatterCake的功能。

总结

装饰器模式角色分配符合设计模式的里氏替换原则,依赖倒置原则,从而使得它具备很强的扩展性,最终满足开闭原则。

  1. 装饰器模式和代理模式的对比

装饰器更强调对被装饰对象自身的扩展,而代理模式更强调代理对象的功能增强。

  1. 代理模式的优点

​ (1).是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。

​ (2).可以通过构造不同的装饰类,对这些类进行排列组合,实现不同的效果。

​ (3).装饰器模式完全遵循开闭原则。

  1. 代理模式的缺点

​ (1).会出现很多的代码,出现更多的类,增加了系统的复杂度。

​ (2).动态装饰时,多层装饰会比较复杂。



The End.
  • 本文作者: 湛天伦
  • 本文链接: https://zhantianlun.com/9.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!