设计模式23篇之 装饰者模式详解

从一个需求入手

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

  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-好点的方案

前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)=>同时违反 ocp原则
在这里插入图片描述

装饰者模式定义

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

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

在这里插入图片描述

装饰者模式原理

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

代码实现

Drink 老祖宗类:

//说明
//1. Drink 是表示饮品,是一个抽象类
abstract class Drink {
  var description = ""
  private var price = 0.0f

  def setDescription(description: String): Unit = {
    this.description = description
  }

  def getDescription(): String = {
    description + " 价格: " + this.getPrice()
  }

  def getPrice(): Float = {
    price
  }

  def setPrice(price: Float): Unit = {
    this.price = price
  }

  //将计算成本的方法做成一个抽象方法cost
  def cost(): Float
}

Coffee子类:

Coffee 继承 Drink 作为缓冲层, 可有可无, 为扩展以防万一.

//在Drink 和  单品咖啡,我做了一个缓冲层
//这里是为了扩展,针对当前项目可以不要
class Coffee extends Drink {
  override def cost(): Float = {
    super.getPrice()
  }
}

Coffee种类: DeCaf

class DeCaf extends Coffee {
  //使用主构造器
  super.setDescription("DeCaf")
  super.setPrice(3.0f)
}

Coffee种类: Espresso

//这个是单品咖啡,在装饰者设计模式中ConcreteComponent
class Espresso extends Coffee {
  //使用主构造器
  super.setDescription("Espresso")
  super.setPrice(6.0f)
}

Coffee种类: LongBlack

class LongBlack extends Coffee {
  //使用主构造器
  super.setDescription("LongBlack")
  super.setPrice(5.0f)
}

Coffee种类: ShortBlack

class ShortBlack extends Coffee {
  //使用主构造器
  super.setDescription("ShortBlack")
  super.setPrice(4.0f)
}

装饰器类:

装饰器父类也必须继承自Drink, 因为要互相传递, 包裹.
注意: cost() 和 getDescription(), 一定要递归实现, 因为不知道包了多少层装饰器!

//这个就是Decorator装饰者
class Decorator extends Drink {
  //obj就是被装饰的对象Drink
  //obj可以是单品咖啡,也可以是单品咖啡+调料的组合
  private var obj: Drink = null

  def this(obj: Drink) {
    this
    this.obj = obj
  }
  //这里我们实现了cost,这里使用了递归方式
  override def cost(): Float = {

    super.getPrice() + obj.cost()
  }
  //获取信息时,也需要递归获取
  override def getDescription(): String = {

    super.getDescription() + "&&" + obj.getDescription()
  }
}

Chocolate装饰器:

class Chocolate(obj: Drink) extends Decorator(obj) {

  super.setDescription("Chocolate")
  //一份巧克力3.0f
  super.setPrice(3.0f)

}

Milk装饰器:

class Milk(obj: Drink) extends Decorator(obj) {
  setDescription("Milk")
  setPrice(2.0f)
}

Soy装饰器:

class Soy(obj: Drink) extends Decorator(obj) {
  setDescription("Soy")
  setPrice(1.5f)
}

main方法测试:

object CoffeeBar {
  def main(args: Array[String]): Unit = {
    println("咖啡bar..")

    val order: Drink = new DeCaf //点DeCaf单品咖啡
    println("order1 price:" + order.cost()) //3.0
    println("order1 desc:" + order.getDescription())

    println("------------------------------------------")
    //  点一份LongBlack,并加入1份Milk 和 2份Chocolate
    var order2: Drink = new LongBlack //5.0
    order2 = new Milk(order2) //2.0
    order2 = new Chocolate(order2) //3.0
    order2 = new Chocolate(order2) //3.0
    order2 = new NewMilk(order2)
    println("order2 price:" + order2.cost()); 
    println("order2 desc:" + order2.getDescription())
  }
}

JDK内置的: java.io.FilterInputStream 也应用了装饰器模式:
注意是Filter, 不是File, 我自己就弄错了…
在这里插入图片描述

发布了125 篇原创文章 · 获赞 237 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_33709508/article/details/103626299