欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JAVA设计模式之装饰者模式

程序员文章站 2022-03-10 17:33:49
咖啡店需要做一个订单系统,以合乎饮料供应要求。 1.最初是这样设计的: 每一种饮料都需要继承该抽象类,并覆写cost()方法。 2.但是购买咖啡时需要考虑到调料的部分,每种咖啡会加不同种的调料,比如蒸奶、豆浆、摩卡或者覆盖奶泡,那么订单系统需要考虑加入不同调料后的价格。因此需要实现不同的子类来定义添 ......

咖啡店需要做一个订单系统,以合乎饮料供应要求。

1.最初是这样设计的:

 1 /**
 2  * 饮料抽象类
 3  *
 4  */
 5 public abstract class Beverage {
 6     
 7     protected String description;
 8     
 9     public String getDescription() {
10         return this.description;
11     }
12     
13     /**
14      * 子类需自定义自己的价格
15      * @return
16      */
17     public abstract double cost();
18     
19 }

每一种饮料都需要继承该抽象类,并覆写cost()方法。

2.但是购买咖啡时需要考虑到调料的部分,每种咖啡会加不同种的调料,比如蒸奶、豆浆、摩卡或者覆盖奶泡,那么订单系统需要考虑加入不同调料后的价格。因此需要实现不同的子类来定义添加不同调料后的价格。大家知道,一种咖啡跟多种调料有多种组合方式,那么多种咖啡和多种调料的组合后,几乎是类爆炸!

 

维护极其困难:

如果某种饮料价格调整;或是新增了某种饮料,怎么办?

 

后来经过改进后,把是否存在某种调料作为饮料的属性,并在饮料抽象类中实现cost方法,子类可以覆写cost方法并设置添加的调料最终确定添加不同调料后的价格:

 1 /**
 2  * 饮料抽象类
 3  *
 4  */
 5 public abstract class Beverage {
 6     
 7     private boolean milk;//牛奶
 8     private boolean soy;//豆浆
 9     private boolean mocha;//摩卡
10     private boolean whip;//奶泡
11     
12     private double milkCost = 0.19;
13     private double soyCost = 0.26;
14     private double mochaCost = 0.29;
15     private double whipCost = 0.17;
16     
17     protected String description;
18     
19     //setter getter method
20 
21     public String getDescription() {
22         return this.description;
23     }
24     
25     public double cost() {
26         double condimentCost = 0.0;
27         if (hasMilk()) {
28             condimentCost += milkCost;
29         }
30         if (hasSoy()) {
31             condimentCost += soyCost;
32         }
33         if (hasMocha()) {
34             condimentCost += mochaCost;
35         }
36         if (hasWhip()) {
37             condimentCost += whipCost;
38         }
39         return condimentCost;
40     }
41     
42 }
43 
44 /**
45  * 低糖咖啡
46  *
47  */
48 public class Decaf extends Beverage {
49     
50     @Override
51     public String getDescription() {
52         return "It is Decaf.";
53     }
54 
55     @Override
56     public double cost() {
57         super.setMilk(true);//添加牛奶调料
58         return 1.99 + super.cost();
59     }
60     
61 }

这样一来,如果有五种咖啡,那么只需要实现五个子类即可,不同的子类可以灵活设置添加不同的调料。

但是这样的设计存在一定的问题:

1)调料价格的改变会使我们改变现有代码;

2)出现新调料,就需要加上新的方法,并改变父类中的cost方法;

3)若出现新的饮料,如红茶,那么新的饮料继承该父类,父类中的调料属性并不合适,如奶泡等;

... ...

 

设计原则:类应该对扩展开放,对修改关闭。

 

装饰者模式思想:以饮料为主体,然后在运行时以调料来“装饰”饮料。

例如客户需要摩卡和奶泡深焙咖啡,那么要做的是:

拿一个深焙咖啡对象;

以摩卡对象装饰;

以奶泡对象装饰;

摩卡和奶泡属于调料,但是也是装饰者,它的类型反映了它装饰的对象,所谓反映,指的是两者类型一致。那么所有调料需要继承Beverage。

 

使用装饰者模式设计的代码:

 1 /**
 2  * 饮料抽象类
 3  *
 4  */
 5 public abstract class Beverage {
 6     
 7     protected String description;
 8     
 9     public String getDescription() {
10         return this.description;
11     }
12     
13     /**
14      * 获取每种饮料的价格
15      * @return
16      */
17     public abstract double cost();
18 }
19 
20 /**
21  * 调料抽象类
22  *
23  */
24 public abstract class Condiment extends Beverage {
25     
26     public abstract String getDescription();
27     
28 }

这里调料继承饮料,仅仅是为了使两者具有相同的类型,并非为了复用父类的行为。

下面是饮料的子类:

 1 /**
 2  * 深焙咖啡
 3  *
 4  */
 5 public class DarkRoast extends Beverage {
 6     
 7     public DarkRoast() {
 8         description = "DarkRoast";
 9     }
10     @Override
11     public double cost() {
12         return 0.19;
13     }
14 
15 }
16 
17 /**
18  * 浓缩咖啡
19  *
20  */
21 public class Espresso extends Beverage {
22     
23     public Espresso() {
24         description = "Espresso";
25     }
26     
27     @Override
28     public double cost() {
29         return 1.99;
30     }
31 
32 }
33 
34 /**
35  * 黑咖啡
36  *
37  */
38 public class HoseBlend extends Beverage {
39     
40     public HoseBlend() {
41         description = "Hose Blend Coffee";
42     }
43     
44     @Override
45     public double cost() {
46         return 0.99;
47     }
48 
49 }

调料(装饰者)子类:

 1 /**
 2  * 摩卡
 3  *
 4  */
 5 public class Mocha extends Condiment {
 6 
 7     private Beverage beverage;
 8     
 9     public Mocha(Beverage beverage) {
10         this.beverage = beverage;
11     }
12 
13     @Override
14     public String getDescription() {
15         return beverage.getDescription() + ", Mocha";
16     }
17 
18     @Override
19     public double cost() {
20         return 0.20 + beverage.cost();
21     }
22 
23 }
24 
25 /**
26  * 豆浆
27  *
28  */
29 public class Soy extends Condiment {
30     
31     private Beverage beverage;
32     
33     public Soy(Beverage beverage) {
34         this.beverage = beverage;
35     }
36 
37     @Override
38     public String getDescription() {
39         return beverage.getDescription() + ", Soy";
40     }
41 
42     @Override
43     public double cost() {
44         return 0.23 + beverage.cost();
45     }
46 
47 }
48 
49 /**
50  * 奶泡
51  *
52  */
53 public class Whip extends Condiment {
54     
55     private Beverage beverage;
56     
57     public Whip(Beverage beverage) {
58         this.beverage = beverage;
59     }
60     
61     @Override
62     public String getDescription() {
63         return beverage.getDescription() + ", Whip";
64     }
65 
66     @Override
67     public double cost() {
68         return 0.69 + beverage.cost();
69     }
70 
71 }

测试代码:

 1 public class ComponentTest {
 2     @Test
 3     public void test() {
 4         Beverage beverage = new Espresso();
 5         System.out.println(beverage.getDescription() + ", $" + beverage.cost());
 6         Beverage beverage2 = new HoseBlend();
 7         beverage2 = new Mocha(beverage2);
 8         beverage2 = new Mocha(beverage2);
 9         beverage2 = new Whip(beverage2);
10         System.out.println(beverage2.getDescription() + ", $" + beverage2.cost());
11         Beverage beverage3 = new DarkRoast();
12         beverage3 = new Soy(beverage3);
13         beverage3 = new Mocha(beverage3);
14         beverage3 = new Whip(beverage3);
15         System.out.println(beverage3.getDescription() + ", $" + beverage3.cost());
16     }
17 }

运行结果:

1 Espresso, $1.99
2 Hose Blend Coffee, Mocha, Mocha, Whip, $2.08
3 DarkRoast, Soy, Mocha, Whip, $1.31

 

java/IO中有很多用到装饰者模式的设计,有兴趣的朋友可以了解下。