装饰者模式

设计模式系列往期精彩文章

无场景不设计,下面有这样一个场景:

咖啡馆订单项目

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

装饰者模式方案1-解决星巴克咖啡订单问题分析

  1. Drink 是一个抽象类,表示饮料
  2. des就是对咖啡的描述, 比如咖啡的名字
  3. cost() 方法就是计算费用,Drink 类中做成一个抽象方法.
  4. Decaf 就是单品咖啡, 继承Drink, 并实现cost
  5. Espress && Milk 就是单品咖啡+调料, 这个组合很多
  6. 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案2-解决星巴克咖啡订单(好点)

前面分析到方案1因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目 的维护性(如图)

装饰者模式

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

方案2-解决星巴克咖啡订单问题分析

  • 方案2可以控制类的数量,不至于造成很多的类
  • 在增加或者删除调料种类时,代码的维护量很大
  • 考虑到用户可以添加多份调料时,可以将hasMilk 返回一个对应int
  • 考虑使用装饰者模式

重点来了

装饰者设计模式

装饰者模式定义

装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

原理图:

装饰者模式说明

装饰者模式原理

  1. 装饰者模式就像是打包快递
    • 主体 比如:衣服 、水果 属于被装饰者 Component
    • 包装 比如:报纸填充、泡沫填充 装饰者 Decorator
  2. Component(无论是主体、还是 包装都需要继承Component 它是一个抽象类)
    • 主体 类似之前提到的Drink
  3. ConcreteComponent(是具体的主体,比如前面讲到的单品coffee)
  4. Decorator(装饰者 比如 调料):这里面聚合和Component(它是一个父类,我们可以放一个具体的子类 装饰者包含被装饰者)
  5. 在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。

好了装饰者设计模式的理论知识差不多了,我们来解决一下一开始的咖啡问题吧。

装饰者设计模式解决咖啡问题原理图

装饰者模式说明:图中Coffee就是上述 5 所提到的缓冲性

  • Drink 类就是前面说的抽象类,Component
  • ShortBlack 就单品咖啡
  • Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)
  • Decorator 的cost 方法进行一个费用的叠加计算,递归的计算价格

综上所述:
装饰者模式下的订单:2份巧克力 + 一份牛奶 的LongBlack

装饰者模式说明:

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

好了,理论知识已经铺垫足了, 我们来开始撸代码吧。

  • Drink
public abstract class Drink {

public String des; // 描述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}

//计算费用的抽象方法
//子类来实现
public abstract float cost();

}
  • Coffee
public class Coffee  extends Drink {

@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice();
}


}
  • Espresso(意大利咖啡)
public class Espresso extends Coffee {

public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
  • LongBlack
public class LongBlack extends Coffee {

public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
  • Decorator
public class Decorator extends Drink {
private Drink obj;

public Decorator(Drink obj) { //组合
// TODO Auto-generated constructor stub
this.obj = obj;
}

@Override
public float cost() {
// TODO Auto-generated method stub
// getPrice 自己价格
return super.getPrice() + obj.cost();
}

@Override
public String getDes() {
// TODO Auto-generated method stub
// obj.getDes() 输出被装饰者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}



}
  • Chocolate
//具体的Decorator, 这里就是调味品
public class Chocolate extends Decorator {

public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 调味品 的价格
}

}
  • Milk
public class Milk extends Decorator {

public Milk(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 牛奶 ");
setPrice(2.0f);
}

}
  • Soy
public class Soy extends Decorator{

public Soy(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 豆浆 ");
setPrice(1.5f);
}

}
  • CoffeeBar
public class CoffeeBar {

public static void main(String[] args) {
// TODO Auto-generated method stub
// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack

// 1. 点一份 LongBlack
Drink order = new LongBlack();
System.out.println("费用1=" + order.cost());
System.out.println("描述=" + order.getDes());

// 2. order 加入一份牛奶
order = new Milk(order);

System.out.println("order 加入一份牛奶 费用 =" + order.cost());
System.out.println("order 加入一份牛奶 描述 = " + order.getDes());

// 3. order 加入一份巧克力

order = new Chocolate(order);

System.out.println("order 加入一份牛奶 加入一份巧克力 费用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());

// 3. order 加入一份巧克力

order = new Chocolate(order);

System.out.println("order 加入一份牛奶 加入2份巧克力 费用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());

System.out.println("===========================");


}

}

好了装饰者设计模式的应用实例就讲到这里了,不知小伙伴是否明白了呢。
接下来给大家讲一讲装饰者模式在JDK源码中的应用吧。

不知道大家是否熟悉Java的IO结构,里面的FileInputStream就是一个装饰者。

请看以下代码块:

 public static void main(String[] args) throws Exception {
DataInputStream dis = new DataInputStream(new FileInputStream("C:\Users\w\Desktop\1.txt"));
System.out.println(dis.read());
dis.close();
}

首先我们从FileInputStream (类似于咖啡问题的单品咖啡) 进行追踪,

装饰者模式

如图所示,我们发现FileInputStream继承了InputStream,而InputStream是一个抽象类,下面有许多子类,里面的方法由子类进行实现。

装饰者模式

InputStream抽象类除了有FileInputStream等子类,还有一个FilterInputStream子类,FilterInputStream又有许多子类(充当装饰者),比如上面所提到的DataInputStream(具体装饰者)

装饰者模式

而这个FilterInputStream(装饰者)中有一个属性InputStream,(被装饰者)

装饰者模式

简单总结:

  1. InputStream 是抽象类, 类似我们前面讲的 Drink
  2. FileInputStream 是  InputStream 子类,类似我们前面的 LongBlack
  3. FilterInputStream  是  InputStream 子类:类似我们前面的Decorator 修饰者
  4. DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk, Soy 等
  5. FilterInputStream 类 有  protected volatile InputStream in; 即含被装饰者
  6. 分析得出在jdk 的io体系中,就是使用装饰者模式

okay  到这里关于装饰者设计模式的应用案例以及源码分析讲到这里就已经结束了,不知看完这篇文章的你是否有所收获。

喜欢的话,点赞关注一下吧!


原文始发于微信公众号(码上遇见你):装饰者模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78994.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!