设计模式——装饰者(装饰器)模式

本文探讨了如何使用装饰者模式解决咖啡订单项目中类爆炸问题,通过将调料内置于 Drink 类并利用装饰者模式动态添加功能,实现了更好的扩展性和维护性。实例展示了如何用 Java 实现 Drink、Coffee、Decorator 和调料类,以及在主程序中灵活定制咖啡配方。
摘要由CSDN通过智能技术生成

开始介绍之前,先分析一个实例:

现有一个咖啡订单项目:

(1)咖啡种类/单品咖啡:Espresso(意大利咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)。

(2)调料:Milk、Soy(豆浆)、Chocolate

(3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便。

(4)使用OO来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。

        针对上述项目,很容易想到可以使用一个抽象类drink来表示饮料,提供抽象的cost方法,各种单品咖啡,以及各种组合继承此抽象类,重写cost方法,便可计算出每种咖啡的费用。但是,此方案会产生很多的类,当我们增加一个单品咖啡或者一个新的调料时,会出现类爆炸现象。

        针对上述问题,做一下改进:将调料内置到Drink类,并且为每种调料提供相应的方法来判断是否添加,这样便不会造成类数量过多,从而提高项目维护性。此方法虽然不会产生类爆炸现象,但是在增加调料的时候代码维护量很大。可进一步使用装饰者模式来改进。

基本介绍

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

原理介绍

装饰者模式就像打包一个快递:

       主体(被装饰者):比如:衣服,零食等等。

       包装(装饰者):比如:纸箱,塑料等等。

其原理图如下:

 如图所示:

(1)Component:主体,比如前面实例中的Drink。

(2)ConcreteComponent:具体的主体,比如前面实例中的各个单品咖啡。

(3)Decorator:装饰者,比如前面实例中的调料。

(4)在如图的Component与ConcreteConponent之间,如果ConcreteConponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。

案例分析

将文章开始的案例使用装饰者模式实现:

// 主体
public abstract class Drink {
    private String design;
    private float price = 0.0f;

    public String getDesign() {
        return design;
    }

    public void setDesign(String design) {
        this.design = design;
    }

    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 Espresso extends Coffee {
    public Espresso() {
        setDesign("意大利咖啡");
        setPrice(10.0f);
    }
}
public class LongBlack extends Coffee {
    public LongBlack() {
        setDesign("美式咖啡");
        setPrice(11.0f);
    }
}

// 装饰者
public class Decorator extends Drink {
    private Drink drink;

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

    @Override
    public float cost() {
        return super.getPrice() + drink.cost();
    }

    @Override
    public String getDesign() {
        return super.getDesign() + " " + super.getPrice();
    }
}


// 具体的装饰者(调料)
public class Milk extends Decorator {
    public Milk(Drink drink) {
        super(drink);
        setDesign("牛奶");
        setPrice(5.0f);
    }
}
public class Soy extends Decorator {
    public Soy(Drink drink) {
        super(drink);
        setDesign("豆浆");
        setPrice(2.0f);
    }
}
public class Chocolate extends Decorator {
    public Chocolate(Drink drink) {
        super(drink);
        setDesign("巧克力");
        setPrice(8.0f);
    }
}


// 主程序(一杯一份牛奶,两份巧克力的美式咖啡)
public static void main(String[] args) {
    Drink longBlack = new LongBlack();
    System.out.println(longBlack.getDesign() + "  " + longBlack.cost());

    longBlack = new Milk(longBlack);
    System.out.println(longBlack.getDesign());
    System.out.println("共计:" + longBlack.cost());

    longBlack = new Chocolate(longBlack);
    System.out.println(longBlack.getDesign());
    System.out.println("共计:" + longBlack.cost());

    longBlack = new Chocolate(longBlack);
    System.out.println(longBlack.getDesign());
    System.out.println("共计:" + longBlack.cost());
}

// 运行结果

/**
美式咖啡  11.0
牛奶 5.0
共计:16.0
巧克力 8.0
共计:24.0
巧克力 8.0
共计:32.0
*/

总结

JDK中FilterInputStream使用了装饰者模式。分析:

 

(1)InputStream是抽象类,类似于前面实例中的drink。

(2)FileInputStream是InputStream子类,类似前面实例中的LongBlack。

(3)FilterInputStream是InputStream子类,类似前面实例中的Decorator修饰者。

(4)DataInputStream是FilterInputStream子类,具体的修饰者,类似前面实例中的调料。

(5)FilterInputStream有protected volatile InputStream in,即含被装饰者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值