定义和特点

装饰者模式主要是用于,再不改变原对象的情况下,给方法增加新的功能。可以多次添加不同的功能和添加功能的顺序。

当然,我同样的装饰者可以给其他的对被装饰的角色对象进行装饰

所以装饰者以及被装饰者都会进行抽象成一个抽象父类。

优点:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果(顺序等不同)
  • 装饰器模式完全遵守开闭原则

缺点:

  • 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

应用场景

  • 当需要给一个现有的类增加附加的职责,而又不能采用生成子类的方法进行扩充时。例如:该类被隐藏或者该类是final或者采用继承的方法会产生大量的子类
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰者模式却很好实现
  • 当对象功能要求可以动态的添加,也可以再动态的撤销时。

代码结构和实现

背景

手抓饼和杂念煎饼,分别加各种配料,然后计算价格

1、抽象被装饰者父类

抽象组件角色(Component)角色:

首先我们是定义一个抽象类Bread,同时定义好基础的功能:

  1. public abstract class Bread {
  2. /**
  3. * 被装饰的的对象
  4. * @return
  5. */
  6. public abstract String getDesc();
  7. /**
  8. * 得到价格
  9. * @return
  10. */
  11. public abstract Integer getPrice();
  12. }

2、下面是两个具体的被装饰者对象

具体组件(Concrete Component)角色:

定义一个被装饰的类,继承Bread抽象类,并实现具体功能:

  1. public class ShreddeBread extends Bread{
  2. @Override
  3. public String getDesc() {
  4. return "手抓饼";
  5. }
  6. @Override
  7. public Integer getPrice() {
  8. return 5;
  9. }
  10. }
  1. public class PancakeBread extends Bread{
  2. @Override
  3. public String getDesc() {
  4. return "杂粮煎饼";
  5. }
  6. @Override
  7. public Integer getPrice() {
  8. return 4;
  9. }
  10. }

3、抽象 装饰者的父类

装饰(Decorator)角色:

定义一个装饰抽象类DeractorBread,继承Bread抽象类,并拥有一个Bread实例

  1. public abstract class DecoractorBread extends Bread{
  2. private Bread bread;
  3. DecoractorBread(Bread bread){
  4. this.bread = bread;
  5. }
  6. /**
  7. * 得到手抓饼配料描述
  8. */
  9. @Override
  10. public String getDesc(){
  11. return bread.getDesc();
  12. }
  13. /**
  14. * 得到价格详情
  15. */
  16. @Override
  17. public Integer getPrice(){
  18. return bread.getPrice();
  19. }
  20. }

4、具体的装饰者

具体装饰(Concrete Decorator)角色:

定义具体装饰类,继承装饰抽象类,并在其中扩展具体组件角色的功能

构造函数,用于传入被装饰者对象。

  1. public class EggshrededBread extends DecoractorBread {
  2. EggshrededBread(Bread bread) {
  3. super(bread);
  4. }
  5. @Override
  6. public String getDesc(){
  7. return "加鸡蛋 " + super.getDesc();
  8. }
  9. @Override
  10. public Integer getPrice(){
  11. return 2 + super.getPrice();
  12. }
  13. }
  1. public class SausageShreddedBread extends DecoractorBread {
  2. SausageShreddedBread(Bread bread) {
  3. super(bread);
  4. }
  5. @Override
  6. public String getDesc(){
  7. return "加火腿肠 " + super.getDesc();
  8. }
  9. @Override
  10. public Integer getPrice(){
  11. return 3 + super.getPrice();
  12. }
  13. }

使用

主要是观察怎么去调用顺序和组合功能使用

  1. public class ShreddeBreadMain {
  2. public static void main(String[] args) {
  3. ShreddeBread shreddeBread = new ShreddeBread();
  4. //手抓饼 价格:5
  5. System.out.println(shreddeBread.getDesc() + " 价格:" + shreddeBread.getPrice());
  6. EggshrededBread eggshrededBread = new EggshrededBread(shreddeBread);
  7. //加鸡蛋 手抓饼 价格:7
  8. System.out.println(eggshrededBread.getDesc() + " 价格:" + eggshrededBread.getPrice());
  9. SausageShreddedBread sausageShreddedBread = new SausageShreddedBread(shreddeBread);
  10. //加火腿肠 手抓饼 价格:8
  11. System.out.println(sausageShreddedBread.getDesc() + " 价格:" + sausageShreddedBread.getPrice());
  12. //先加鸡蛋再加火腿肠
  13. SausageShreddedBread sausageShreddedBreadOne = new SausageShreddedBread(eggshrededBread);
  14. //加火腿肠 加鸡蛋 手抓饼 价格:10
  15. System.out.println(sausageShreddedBreadOne.getDesc() + " 价格:" + sausageShreddedBreadOne.getPrice());
  16. // 往下这是杂念煎饼的
  17. PancakeBread pancakeBread = new PancakeBread();
  18. //杂粮煎饼 价格:4
  19. System.out.println(pancakeBread.getDesc() + " 价格:" + pancakeBread.getPrice());
  20. EggshrededBread eggPancake = new EggshrededBread(pancakeBread);
  21. //加鸡蛋 杂粮煎饼 价格:6
  22. System.out.println(eggPancake.getDesc() + " 价格:" + eggPancake.getPrice());
  23. SausageShreddedBread sausagePancake = new SausageShreddedBread(eggPancake);
  24. //加火腿肠 加鸡蛋 杂粮煎饼 价格:9
  25. System.out.println(sausagePancake.getDesc() + " 价格:" + sausagePancake.getPrice());
  26. }
  27. }

模式的扩展

装饰者模式的四种角色不是任何时候都必须存在的。有些情况也可以进行简化。

  • 如果只有一个具体构件没有抽象构件的时候,可以让抽象装饰者继承具体构件。
  • 如果只有一个具体装饰者,可以将抽象装饰者和具体装饰者合并。