zoukankan      html  css  js  c++  java
  • [Head First设计模式]山西面馆中的设计模式——装饰者模式

    引言

    在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式。这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧。吃货有吃货的方式来理解......这里先将书中讲到的例子放在前面,理论的东西,讲的还是比较具体的,只是觉得咖啡的例子不是太好理解,lz很土,几乎没喝过咖啡,不知道什么摩卡啊......,还是中国特色的例子更好理解。

    为什么学设计模式?

    答:觉得会设计模式的人,不仅仅是码农,更像艺术家!

    为什么现在学设计模式?

    答:不求精通,但求认识,接触过不少项目,有设计模式,而不认识,是我的损失,体会不到他的妙处,但不求它认识我。

    装饰者到底装饰谁呢?

    类,还是对象?

    装饰者模式定义

    动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。(由定义可知,装饰对象的)

    实例分析

    星巴兹咖啡订单管理系统 管理、计算各种饮料的售价。

     若顾客根据个人喜好,添加不同的调料,那么系统就要有根据调料的不同来计算价格,按照原来的设计,必定会出现下面的情况。

     第一种方案设计:继承

     有多少种口味的咖啡,你就得建多少种对应的类。烦不?

    第二种方案设计:

    思考

     当哪些因素改变时会影响这个设计?

    调料价钱的改变会使我们更改现有代码。

    一旦出现新的调料,我们就需要加上新的方法,并改变超类中的cost()方法。

    以后可能会开发出新饮料,对于这些饮料而言(冰茶),某些调料可能并不适合,但是在这个设计方式中,Tea子类仍将继承那些不合适的方法,比如:hasWhip()。
    如何顾客想要双倍摩卡,怎么办?

    ......

     设计原则(Open-Closed Principle)

     类应该对扩展开放,对修改关闭。

    我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,其好处在于:这样的设计具有弹性,可以应对改变,可以接受新的功能来应对改变的需求。

    如何让设计的每个部分都遵循开放-关闭原则?

    这通常是无法做到的。要让OO设计同时具有开放性和关闭性,又不修改现有的代码,需要花费许多时间和努力。一般来说,我们没有足够的精力把设计的每个部分都这么设计,这可能只是一种浪费。
    遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度。你需要把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。

     装饰者模式

    星巴兹咖啡订单管理系统——使用装饰者模式

    以饮料(Beverage)为主体,然后在运行时以调料(Condiment)来装饰(decorate)饮料

    比如,顾客想要摩卡和奶泡深焙咖啡,那么:
    取出一个深焙咖啡(DarkRoast)对象
    以摩卡(Mocha)对象装饰它
    以奶泡(Whip)对象装饰它
    调用cost方法,并依赖委托(delegrate)将调料的价格加上去

     步骤:

     

    “装饰者模式”——特点

    装饰者和被装饰对象具有相同的超类型
    可以用一个或多个装饰者包装一个对象
    由于装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装)的场合,都可以用装饰过的对象代替她
    装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的
    对象可以在任何时候被装饰,所以可以在运行时动态的、不限量的用需要的装饰者来装饰对象

     装饰者模式类图关系

    星巴兹咖啡销售系统装饰者模式类图关系

    思考:为什么Decorate类扩展自Component类?

    装饰者和被装饰者必须是一样的类型,我们在此使用继承达到“类型匹配”
    类型匹配意味着装饰者和被装饰者具有相同的接口,从而装饰者可以取代被装饰者
    新的行为并不是继承自超类,而是由组合对象得到,即所有饮料和调料可以更有弹性的加以混合和匹配
    我们可以在任何时候,实现新的装饰者增加新的行为。如果依赖继承,每当需要新行为时,必须修改代码
    Component类型可以使用抽象类,也可以使用接口

    问题:如果有一张订单:“双倍摩卡豆浆奶泡拿铁咖啡”,应该如何进行设计?

    这里代码的具体实现就不再写了,网上这样的代码太多了?

    山西面馆中的“装饰者模式”

     先上类图:直观形象

     代码实现:

     1      /// <summary>
     2     ///抽象类 食物基类 其他类都继承自该类
     3     /// </summary>
     4     public abstract class Food
     5     {
     6         protected string description = "未知的饭";
     7         public virtual string GetDescription()
     8         {
     9             return this.description;
    10         }
    11         public abstract double Cost();
    12     }
    1       /// <summary>
    2     /// 所有配料的基类 继承自Food类 保持类型一致
    3     /// </summary>
    4     public abstract class Ingredients : Food
    5     {
    6        
    7     }

    具体的配料:鸡蛋,西红柿类继承配料类

     1 public class Egg : Ingredients
     2     {
     3         Food meal;
     4         public Egg(Food meal)
     5         {
     6             this.meal = meal;
     7 
     8         }
     9         public override double Cost()
    10         {
    11             return 3.0 + meal.Cost();
    12         }
    13 
    14         public override string GetDescription()
    15         {
    16             return "鸡蛋"+meal.GetDescription();
    17         }
    18     }
     1 public class Tomato : Ingredients
     2     {
     3         Food meal;
     4         public Tomato(Food meal)
     5         {
     6             this.meal = meal;
     7 
     8         }
     9         public override double Cost()
    10         {
    11             return 3.0 + meal.Cost();
    12         }
    13 
    14         public override string GetDescription()
    15         {
    16             return "西红柿" + meal.GetDescription();
    17         }
    18     }

    所谓的component类(Noodle)继承自Food类

     1   public class Noodle : Food
     2     {
     3         public Noodle()
     4         {
     5             description = "";
     6         }
     7         public override double Cost()
     8         {
     9             return 3.0;
    10         }
    11     }

    控制台测试程序:

     1  class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             //创建被装饰的对象 noodle
     6             Food f1 = new Noodle();
     7             //用鸡蛋装饰
     8             f1 = new Egg(f1);
     9             //用西红柿装饰
    10             f1 = new Tomato(f1);
    11             Console.WriteLine(f1.GetDescription() + "	" + f1.Cost() + "");
    12             Console.Read();
    13         }
    14     }

    结果:

    思考:装饰者模式是否对修改封闭,对扩展开放呢?

    那么,测试一下,加入现在我想要一份酱爆鸡丁盖浇饭,该怎么实现?

     1  public class Rice : Food
     2     {
     3         public Rice()
     4         {
     5             description = "盖浇饭";
     6         }
     7         public override double Cost()
     8         {
     9             return 5.0;
    10         }
    11     }
     1  public class Chicken : Ingredients
     2     {
     3         Food meal;
     4         public Chicken(Food meal)
     5         {
     6             this.meal = meal;
     7         }
     8         public override string GetDescription()
     9         {
    10             return "酱爆鸡丁" + meal.GetDescription();
    11         }
    12         public override double Cost()
    13         {
    14             return 9.0 + meal.Cost();
    15         }
    16     }
     1  class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             //创建被装饰的对象 noodle
     6             Food f1 = new Noodle();
     7             //用鸡蛋装饰
     8             f1 = new Egg(f1);
     9             //用西红柿装饰
    10             f1 = new Tomato(f1);
    11             Console.WriteLine(f1.GetDescription() + "	" + f1.Cost() + "");
    12             //创建被装饰的对象 米饭
    13             Food f2 = new Rice();
    14             //用酱爆鸡丁 装饰(将配料合并了)
    15             f2 = new Chicken(f2);
    16             Console.WriteLine(f2.GetDescription() + "	" + f2.Cost() + "");
    17             Console.Read();
    18         }
    19     }


    结果:

    很容易扩展吧?只需要加两个类继承对应的类就可以了,原来的内部代码不需要修改,就可以实现

    总结

    在我们的代码中,应该允许行为遵循对扩展开放-对修改关闭的原则,这样就可以无需修改现有的代码就可以实现我们扩展的功能。

    装饰者模式意味着一群装饰者类,这些类用来包装具体组件。

    装饰者反映出被装饰者的组件类型(具有相同的类型)

    装饰者可以在被装饰者的行为前面或后面加上自己的行为,甚至将被装饰者的行为整个取代,而达到特定的目的。

    可以用无数个装饰者包装一个组件。

    装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。

    装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂。

    (最近一直在看设计模式,上班下班的路上想,吃饭的时候也想,不求精通,只求在项目中遇到了,能认出他,不求他认识我)

    参考书:

    Head First 设计模式

  • 相关阅读:
    JAXB
    Servlet编码和解码
    FileUpload
    DataSource
    20200821 二分搜索
    火狐账户如何在Android手机端与电脑端同步
    20200807 贪心;排队接水;[AHOI2018初中组]分组;国王的游戏
    20200802 高精度问题 阶乘之和
    20200803 牛客多校赛
    20200207
  • 原文地址:https://www.cnblogs.com/wolf-sun/p/3468100.html
Copyright © 2011-2022 走看看