一、提出需求
咖啡馆订单系统项目(咖啡馆):
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
每天进步一点点,也许某一天你也会变得那么渺小!!!