第 17 章 设计模式(3 >>> 装饰者模式)

一、提出需求

咖啡馆订单系统项目(咖啡馆):

1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡);
2. 调料:Milk、Soy(豆浆)、Chocolate;
3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便;
4. 使用OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。

二、方案设计

1、方案一:较差的方案

在这里插入图片描述
方案一小结和分析

1. Drink是一个抽象类,表示饮料;
2. description就是描述,比如咖啡的名字等;
3. cost就是计算费用,是一个抽象方法;
4. Decaf等等就是具体的单品咖啡,继承Drink,并实现cost方法;
5. Espresso&&Milk等等就是单品咖啡+各种调料的组合,这个会很多...;
6. 这种设计方式时,会有很多的类,并且当增加一个新的单品咖啡或者调料时,类的数量就会倍增(类爆炸)。
2、方案二:比较好点的方案
	前面分析到方案一,因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样
就不会造成类数量过多。从而提高项目的维护性。

方案设计如下图:

说明: milk,soy,choclate 可以设计为Boolean,表示是否要添加相应的调料。

在这里插入图片描述
方案二的问题分析

1. 方案2可以控制类的数量,不至于造成过多的类;
2. 在增删调料种类时,代码维护量仍然很大;
3. 考虑到添加多份调料时,可以将Boolean改成Int。

三、装饰者模式原理

1、方案设计图

在这里插入图片描述

2、针对上面的图说明
1. 装饰者模式就像打包一个快递:
	主体:比如:陶瓷、衣服 (Component)
	包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
2. Component主体:比如类似前面的Drink;
3. ConcreteComponent和Decorator
	ConcreteComponent:具体的主体,比如前面的各个单品咖啡
	Decorator: 装饰者,比如各调料.
4. 在上面的图中Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,
将共有的部分提取出来,抽象层一个类。

四、装饰者模式定义

1. 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了
开闭原则(ocp);
2. 这里提到的动态的将新功能附加到对象和ocp原则,接下来会在应用实例上会以代码的形式体现。

五、用装饰者模式重新设计方案

1、方案设计图

在这里插入图片描述

2、方案说明
1. Drink这个超类和前面基本一样;
2. ShortBlack等单品咖啡的设计也和前面一样;
3. Decorator是一个装饰类,含一个被装饰的对象(Drink obj);
4. Decorator的cost 进行费用的叠加,递归计算出价格。
3、订单

装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack
在这里插入图片描述
订单说明:

1. Milk包含了LongBlack;
2. 一份Chocolate包含了(Milk+LongBlack);
3. 一份Chocolate包含了(Chocolate+Milk+LongBlack);
4. 这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护。

订单分析

1. 有一份 Milk + LongBlack【milk装饰LongBlack】;
2. 使用一份Chocolate装饰【一份 Milk + LongBlack 】;
3. 使用一份chocolate装饰【chocolate+milk+longBlack】;
4. 当计算cost要递归的计算。

五、装饰者模式咖啡订单项目应用实例代码

1、代码结构图

在这里插入图片描述

2、示例代码
================================abstract class Drink 超类================================
package com.lj.akka.decoratormode.coffeebar
/**
  * @author Administrator
  * @create 2020-03-28
  */
abstract class Drink {
    // coffee描述
    private var _description: String = _
    // _description的setter与getter方法
    def description: String = _description
    def description_=(value: String): Unit = {
      _description = value
    }
    // coffee价格
    private var _price: Float = _
    // _price的setter与getter方法
    def price: Float = _price
    def price_=(value: Float): Unit = {
      _price = value
    }
    def cost(): Float
}
==================================class Coffee extends Drink=================================
package com.lj.akka.decoratormode.coffeebar.differentcoffee
import com.lj.akka.decoratormode.coffeebar.Drink
/**
  * @author Administrator
  * @create 2020-03-28
  */
class Coffee extends Drink {
    override def cost(): Float = {
        super.price
    }
}
==================================class DeCaf extends Coffee=================================
// 说明:其它种类的咖啡,它这个一样的写法都是集成Coffee类,并使用主构造器完成价格和名称描述
package com.lj.akka.decoratormode.coffeebar.differentcoffee
/**
  * @author Administrator
  * @create 2020-03-28
  */
class DeCaf extends Coffee {
    // 使用主构造器
    super.description_=("DeCaf Coffee")
    super.price_=(10.0f)
}
===============================class Decorator extends Drink==============================

package com.lj.akka.decoratormode.coffeebar.coffeedecorator
import com.lj.akka.decoratormode.coffeebar.Drink
/**
  * @author Administrator
  * @create 2020-03-28
  */
class Decorator extends Drink{
    private var drink: Drink = null
    def this(drink: Drink) {
        this
        this.drink = drink
    }
    override def cost(): Float = {
        super.price + drink.cost()
    }
    // 获取信息是也需要递归获取
    override def description: String = {
        super.description + " && " + drink.description
    }
}
===============class Chocolate(drink: Drink) extends Decorator(drink)===============
// 其它的添加的配料写法都一样
package com.lj.akka.decoratormode.coffeebar.coffeedecorator
import com.lj.akka.decoratormode.coffeebar.Drink
/**
  * @author Administrator
  * @create 2020-03-28
  */
class Chocolate(drink: Drink) extends Decorator(drink) {
    super.description_=("Chocolate")
    super.price_=(5f)
}
============================咖啡点单:object CoffeeBar============================
package com.lj.akka.decoratormode
import com.lj.akka.decoratormode.coffeebar.Drink
import com.lj.akka.decoratormode.coffeebar.coffeedecorator.{Chocolate, Milk}
import com.lj.akka.decoratormode.coffeebar.differentcoffee.{DeCaf, LongBlack}
/**
  * @author Administrator
  * @create 2020-03-28
  */
object CoffeeBar {
    def main(args: Array[String]): Unit = {
        val order01: Drink = new DeCaf
        val order01_name = order01.description
        val order01_price = order01.cost()
        // 001顾客点的是DeCaf Coffee,价格10.0RMB.
        println(s"001顾客点的是${order01_name},合计${order01_price}RMB.")
        var order02: Drink = new LongBlack
        order02 = new Milk(order02)
        order02 = new Chocolate(order02)
        order02 = new Milk(order02)
        val order02_name = order02.description
        val order02_price = order02.cost()
        // 002顾客点的是Chocolate && Chocolate && Chocolate && LongBlack Coffee,合计26.0RMB.
        println(s"002顾客点的是${order02_name},合计${order02_price}RMB.")
    }
}

小结:从上面代码可以看出装饰者模式在代码维护上更加的复合OCP原则,使代码更优雅。

3、Java中装饰者模式的经典使用
Java的IO结构,FilterInputStream就是一个装饰者

在这里插入图片描述

对以前的知识回顾,加深基础知识!
学习来自:北京尚硅谷韩顺平老师—尚硅谷大数据技术之Scala
每天进步一点点,也许某一天你也会变得那么渺小!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值