千千梦丶琪 发表于 2023-9-20 10:19:50

装饰者模式

装饰者模式

咖啡案例引入


[*]1.咖啡种类,有意大利咖啡(Espresso),ShortBlack,美式咖啡(LongBlack),无因咖啡(Decaf)。
[*]2.调料,Milk,Soy(豆浆),Chocolate(巧克力)。
[*]3.要求再扩展新的咖啡种类时,具有良好的扩展性,改动方便,维护方便。
[*]4.客户可以点单品咖啡,也可以点单品 + 调料组合。
方案1类图

https://img2023.cnblogs.com/blog/2883613/202309/2883613-20230917002302827-67369858.png
方案1类图分析


[*]1.Drink是抽象类,表示饮料。
[*]2.description是对咖啡的描述,如咖啡的名字。
[*]3.cost()是计算费用,Drink类中定义为抽象的。
[*]4.Decaf等就是单品咖啡,继承Drink,重写cost()。
[*]5.Espresso && Milk就是单品咖啡 + 调料,这个组合很多。
[*]6.这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会暴增,会出现类爆炸。
方案2类图

https://img2023.cnblogs.com/blog/2883613/202309/2883613-20230917002909835-1609101317.png
类图分析

方案1会出现,咖啡单品 + 调料组合会造成类的倍增,因此可以做改进,将调料内置在Drink中,这样就不会出现类爆炸,提高项目的可维护性。说明milk,soy,chocolate可以设计为Boolean,表示是否要添加相应的调料。

[*]1.方案2可以控制类数量,避免类爆炸情况。
[*]2.但在增加和删除调料时,代码的维护量还是很大。
[*]3.用户点多份调料的情况,可以返回一个int值,代表份数。
[*]4.可以考虑使用装饰者模式。
基本介绍


[*]1.装饰者模式(Decorator Pattern),动态的将新功能附加到对象上。在对象扩展方面,比继承更有弹性,装饰者模式也提醒了开闭原则(ocp)。
[*]2.这里提到的动态附加新功能到对象上,和ocp,在案例中有体现。
装饰者原理举例


[*]1.装饰者模式就像打包一个快递。主体是衣服,陶瓷等,包装,比如塑料,泡沫,木板等。这样构成一个完整的快递。
[*]2.主体,就是Drink(被装饰者),包装就是Decorator(装饰者)。
装饰者模式实现案例类图

https://img2023.cnblogs.com/blog/2883613/202309/2883613-20230917003800322-197303421.png
说明,类图中的Decorator应该是继承Drink,然后在Decorator中组合一个 Drink drink属性;

//饮品
public abstract class Drink {

    private String des;
    private float price;

    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();
}
//咖啡父类
public class Coffee extends Drink {
    @Override
    public float cost() {
      return super.getPrice();
    }
}
public class Decaf extends Coffee{
    public Decaf(){
      setDes(" 无因咖啡 ");
      setPrice(3.0f);
    }
}
public class Espresso extends Coffee{
    public Espresso(){
      setDes(" 意大利咖啡 ");
      setPrice(6.0f);
    }
}
public class LongBlack extends Coffee{
    public LongBlack(){
      setDes(" 美式咖啡 ");
      setPrice(5.0f);
    }
}
public class ShortBlack extends Coffee{
    public ShortBlack(){
      setDes(" 黑咖啡 ");
      setPrice(5.0f);
    }
}
/**
* @author 长名06
* @version 1.0
* 修饰者 是调料的父类
*/
public class Decorator extends Drink{

    private Drink drink;//组合一个drink

    public Decorator(Drink drink){
      this.drink = drink;
    }

    @Override
    public float cost() {
      //super.getPrice()代表小料的价格 drink.cost()代表饮料的价格
      return super.getPrice() + drink.cost();
    }

    @Override
    public String getDes() {
      //重写描述方法
      return super.getDes() + getPrice() + " && " + drink.getDes();
    }
}
public class Milk extends Decorator{
    public Milk(Drink drink){
      super(drink);
      setDes(" 牛奶 ");
      setPrice(2.0f);
    }
}
public class Soy extends Decorator{
    public Soy(Drink drink){
      super(drink);
      setDes(" 豆浆 ");
      setPrice(3.0f);
    }
}
public class Chocolate extends Decorator{
    public Chocolate(Drink drink){
      super(drink);
      setDes(" 巧克力 ");
      setPrice(3.0f);
    }
}
//测试
public class Client {
    public static void main(String[] args) {
      //下订单,买2份巧克力 + 1份牛奶的LongBlack咖啡
      //1.买一份LongBlack咖啡
      //咖啡要用Drink接收,不能使用自己的咖啡类型
      //因为被小料修饰过的咖啡的类型是对应的小料类型
      //如果用自己的咖啡类型,就不能完成 修饰过的咖啡 赋给原有的咖啡了
      Drink longBlack = new LongBlack();
      System.out.println("LongBlack咖啡价格 " + longBlack.cost());
      System.out.println("LongBlack咖啡描述 " + longBlack.getDes());

      //2.给LongBlack咖啡,加一个牛奶小料
      longBlack = new Milk(longBlack);
      System.out.println("加一个牛奶小料的LongBlack咖啡价格 " + longBlack.cost());
      System.out.println("加一个牛奶小料的LongBlack咖啡描述 " + longBlack.getDes());

      //3.给LongBlack咖啡,加一个巧克力小料
      longBlack = new Chocolate(longBlack);
      System.out.println("加一个牛奶小料 加一个巧克力小料的LongBlack咖啡价格 " + longBlack.cost());
      System.out.println("加一个牛奶小料 加一个巧克力小料的LongBlack咖啡描述 " + longBlack.getDes());

      //4.给LongBlack咖啡,加两个巧克力小料
      longBlack = new Chocolate(longBlack);
      System.out.println("加一个牛奶小料 加两个巧克力小料的LongBlack咖啡价格 " + longBlack.cost());
      System.out.println("加一个牛奶小料 加两个巧克力小料的LongBlack咖啡描述 " + longBlack.getDes());

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

      Drink decaf = new Decaf();
      System.out.println(decaf.cost());
      System.out.println(decaf.getDes());

      decaf = new Soy(decaf);
      System.out.println("加一个豆浆小料的LongBlack咖啡价格 " + decaf.cost());
      System.out.println("加一个豆浆小料的LongBlack咖啡描述 " + decaf.getDes());
    }
}装饰者模式在JDK应用分析

https://img2023.cnblogs.com/blog/2883613/202309/2883613-20230917004407297-82317582.png
import java.io.DataInputStream;
import java.io.FileInputStream;

/**
* @author 长名06
* @version 1.0
*/
public class AnalysisDecoratorIO {
    public static void main(String[] args) throws Exception{
      //JDK源码中就使用到了装饰者模式
      //1.InputStream是抽象类,类似我们前面讲的Drink
      //2.FileInputStream是InputStream子类,类似我们前面的 DeCaf, LongBlack
      //3. FilterInputStream是InputStream子类:类似我们前面的Decorator修饰者
      //4. DataInputStream是 FilterInputStream子类,具体的修饰者,类似前面的Milk, Soy 等
      // 5.FilterInputStream类有 protected volatile InputStream in属性 ;即含被装饰者
      //6.分析得出在jdk 的io体系中,就是使用的装饰者模式
      DataInputStream dis = new DataInputStream(new FileInputStream("d:\\abc.txt"));
    }
}只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 装饰者模式