zoukankan      html  css  js  c++  java
  • headFirst设计模式——装饰者模式

    一旦你熟悉了装饰的技巧,你将能够不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。

    一、引入

      星巴兹咖啡因为扩张速度太快了,他们准备更新订单系统,以合乎他们的饮料供应需求。

      原先的设计类:

      

      购买咖啡时,可以加入各种调味。星巴兹会根据加入的调料收取不同的费用。

      

       违反设计原则:封装变化;多用组合,少用继承;

      改法一:应用实例变量和继承。

        1、在基类上加上调料实例,并在cost()方法中实现基本逻辑,让子类继承。

        2、子类可以在其基础价格上加上超类价格(各种调料的价格)。

      思考:一旦出现新的调料,我们要修改基类cost方法。

          出现新的饮品,某些调料可能并不适合。

          万一顾客想要双倍奶泡呢。

      利用组合的方式,可以把设计超类时没有想到的职责加在对象上。而且,可以不用修改原来的代码。

      设计原则:

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

        允许类容易扩展,在不修改先有代码的情况下,就可搭配新的行为。(在需要的地方遵循 开放-关闭原则,过度的使用这个原则也是一种浪费,还会导致代码变得复杂且难以理解)。

    二、装饰者模式

      以饮料为主题,然后在运行时以调料来“装饰”(decorate)饮料。

      比如说用户想要摩卡和奶泡深焙咖啡,那么,要做的是:

      1、拿一个深焙咖啡(DarkRoast)的对象

      2、以摩卡(Mocha)对象装饰它

      3、以奶泡(whip)对象装饰它

      4、调用cost()方法,并依赖委托(delegate)将调料的价钱加上去

      

       

      小结:

        1、装饰者和被装饰者对象有相同的超类型。

        2、你可以用一个或多个装饰者包装一个对象。

        3、既然装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。

        4、装饰者可以在所委托被装饰者之前/或之后,加上自己的行为,以达到特定的目的

        5、对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

      装饰者模式:

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

      

      星巴兹咖啡

      

    抽象组件

     1 // Beverage 是一个抽象类
     2 public abstract class Beverage {
     3 
     4     String description = "Unknown Beverage";
     5     
     6     // 对饮品的描述
     7     public String getDescription() {
     8         return description;
     9     }
    10     
    11     // cost要在子类中实现(都要实现)
    12     public abstract double cost();
    13 }

    抽象装饰者——调料Condiment抽象类

    1 public abstract class CondimentDecorator extends Beverage {
    2 
    3     public abstract String getDescription();
    4 
    5 }

    具体组件——饮品

     1 public class Espresso extends Beverage {
     2 
     3     public Espresso() {
     4         description = "Espresso";
     5     }
     6 
     7     @Override
     8     public double cost() {
     9         return 2.99;
    10     }
    11 
    12 }

    具体装饰者——调料

     1 public class Milk extends CondimentDecorator {
     2 
     3     Beverage beverage;// 用一个实例变量记录饮料,也就是被装饰者
     4     
     5     public Milk(Beverage beverage) {
     6         this.beverage = beverage;// 让被装饰者被记录到实例变量中。把饮料当做构造器参数,再由构造器将此饮料记录在实例变量中
     7     }
     8 
     9     @Override
    10     public String getDescription() {
    11         // 我们希望描述的不止是饮料,而是完整地连调料都描述出来。
    12         return beverage.getDescription()+",Milk";// 我们利用委托的做法,得到一个叙述,然后再加上其他的附加描述。
    13     }
    14 
    15     @Override
    16     public double cost() {
    17         return beverage.cost() + 0.5;// 调用委托给被装饰对象,以计算价钱,然后再加上调料的价钱。
    18     }
    19 
    20 }

    测试

     1 public class StarbuzzCoffee {
     2 
     3     public static void main(String[] args) {
     4         Beverage beverage = new Espresso();
     5         System.out.println(beverage.getDescription()+" $"+beverage.cost());//Espresso $2.99
     6         
     7         Beverage darkRoast = new DarkRoast();
     8         Beverage darkMocha = new Mocha(darkRoast);
     9         Beverage dark2Mocha = new Mocha(darkMocha);
    10         Beverage dark2MochaWhip = new Whip(dark2Mocha);
    11         System.out.println(dark2MochaWhip.getDescription()+" $"+dark2MochaWhip.cost());
    12 //        DarkRoast,Mocha,Mocha,Whip $4.7299999999999995
    13         
    14         Beverage houseBlend = new HouseBlend();
    15         houseBlend = new Soy(houseBlend);
    16         houseBlend = new Mocha(houseBlend);
    17         houseBlend = new Whip(houseBlend);
    18         System.out.println(houseBlend.getDescription() + " $"+houseBlend.cost());
    19 //        HouseBlend,Soy,Mocha,Whip $3.2300000000000004
    20         
    21     }
    22     
    23 }

      思考:

        1、如果碰到打折呢

        2、如果按大中小杯收费

        3、想要展示Double Mocha 而不是Mocha,Mocha

      优点:

        为设计注入弹性

        透明地插入装饰者,客户程序甚至不需要知道它是在和装饰者打交道

      问题:

        不易理解(要明确这些小类(调料)都是用来包装某个类(饮料)的)

        插入装饰者过多,容易混淆(插入装饰者时要谨慎)

        采用装饰者在实例化组件时,增加代码难度。

        一旦使用装饰者模式,不只需要实例化组件,还要把次组件包装进装饰者中。

      解决:

        工厂(Factory)模式和生成器(Builder)模式,隐藏代码难度。

      

      设计原则:

        1、封装变化

        2、多用组合,少用继承

        3、针对接口编程,不针对实现编程

        4、为交互对象之间的松耦合设计而努力

        5、对扩展开放,对修改关闭

      

      『装饰者模式』动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。

      

      继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。

      在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。

      组合和委托可用于在运动时动态地加上新的行为。

      除了继承,装饰者模式也可以让我们扩展行为。

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

      装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。

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

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

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

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

  • 相关阅读:
    MVC View基础(转)
    ASP.NET MVC:自定义 Route 生成小写 Url(转)
    python抓取360百科踩过的坑!
    数组循环移位的几种解法
    volatile型变量自增操作的隐患
    HAWQ技术解析(十八) —— 问题排查
    系列网页。前端开发流程
    Python图像处理(8):边缘检測
    析构函数
    Spring(八)编码剖析@Resource注解的实现原理
  • 原文地址:https://www.cnblogs.com/onroad2019/p/12911755.html
Copyright © 2011-2022 走看看