Java设计模式——装饰者模式

问题引出

如果我们想喝一杯咖啡,而咖啡又分为意大利浓咖啡(Espresso),美式咖啡,无因咖啡,同时又有牛奶、豆浆(别问我为什么加豆浆,加就对了)、巧克力等配料,所以我们的咖啡就有很多种选择,这时程序应该怎么设计?

很蠢的方法

  • Drink是一个抽象类,表示饮料

  • des是对咖啡的描述,比如说咖啡的名字

  • cost()方法时计算费用

  • 咖啡种类+配料,这个组合有很多,每一个组合为一个类

问题:这样设计会有很多的类,相当于一个排列组合,最终会造成类爆炸(就是有很多类的意思),会维护的你怀疑人生

不是那么蠢的方法

  • 将配料内置到Drink类中,通过方法设置是否添加配料,这样就不会造成类的数量过多了

问题:在增加或者删除配料种类时,代码的维护量很大

那么重点来了,有没有什么不蠢的方法🐴?
答:那就是装饰者模式

装饰者模式

简介

  • 通过委托机制,复用系统中的各个组件,在运行时,可以将这些功能组件进行叠加,构造一个拥有这些组件所有功能的对象

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

注:继承是一种紧密耦合,任何父类都会影响其子类,不利于维护。而委托则是松散耦合,只要接口不变,委托类的改动并不会影响其上层对象

使用装饰者模式解决咖啡问题
在这里插入图片描述

// 抽象饮品类
public abstract class Drink {
    public String des;  // 描述
    private float price = 0.0f;

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

    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 class Coffee extends Drink {
    @Override
    public float cost() {
        return super.getPrice();
    }
}
// 具体咖啡类:美式咖啡
public class Espresso extends Coffee {

    public Espresso() {
        setDes("意大利浓咖啡");
        setPrice(10.0f);
    }
}
// 具体咖啡类:美式咖啡
public class LongBlack extends Coffee {

    public LongBlack() {
        setDes("美式咖啡");
        setPrice(8.0f);
    }
}
// 具体咖啡类:无因咖啡
public class Decaf extends Coffee {

    public Decaf() {
        setDes("无因咖啡");
        setPrice(12.0f);
    }
}
// 装饰者类
public class Decorator extends Drink {

    private Drink obj;

    public Decorator(Drink obj) {   // 组合
        this.obj = obj;
    }

    @Override
    public float cost() {
        // getPrice()为自己的价格
        return super.getPrice() + obj.cost();
    }

    @Override
    public String getDes() {
        return des + " " + getPrice() + "&&" + obj.getDes();
    }

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

    public Chocolate(Drink obj) {
        super(obj);
        setDes("巧克力");
        setPrice(3.0f); // 配料的价格
    }
}
// 具体的Decorator,这里就是调味品牛奶
public class Milk extends Decorator {
    public Milk(Drink obj) {
        super(obj);
        setDes("牛奶");
        setPrice(1.5f);
    }
}
// 具体的Decorator,这里就是调味品豆浆
public class Soy extends Decorator {
    public Soy(Drink obj) {
        super(obj);
        setDes("豆浆");
        setPrice(1.0f);
    }
}
// 客户端,模拟客人点咖啡
public class CoffeeBar {
    public static void main(String[] args) {
        // 装饰者模式下的订单:两份巧克力+一份牛奶的美式咖啡

        // 1、点一份美式咖啡
        Drink order = new LongBlack();
        System.out.println("订单一:" + order.getDes()+ ",价格为" +order.getPrice());

        // 2、加入一份牛奶
        order = new Milk(order);
        System.out.println("加入一份牛奶过后:" + order.getDes()+ ",价格为" +order.cost());

        // 3、加入一份巧克力
        order = new Chocolate(order);
        System.out.println("加入一份巧克力过后:" + order.getDes()+ ",价格为" +order.cost());

        // 3、再加入一份巧克力
        order = new Chocolate(order);
        System.out.println("再加入一份巧克力过后:" + order.getDes()+ ",价格为" +order.cost());

    }
}
// 运行结果
订单一:美式咖啡,价格为8.0
加入一份牛奶过后:牛奶 1.5&&美式咖啡,价格为9.5
加入一份巧克力过后:巧克力 3.0&&牛奶 1.5&&美式咖啡,价格为12.5
再加入一份巧克力过后:巧克力 3.0&&巧克力 3.0&&牛奶 1.5&&美式咖啡,价格为15.5

优势:假如此时我们再加入一个咖啡类,只需要使这个类继承Drink类就可以直接使用了,配料同理

扩展
在JDK的实现中,有不少组件也是利用装饰者模式实现,其中最经典的例子是InputStream和OutputStream类族的实现。以OutputStream为例,OutputStream对象提供的方法比较简单,功能也比较弱,但通过各种装饰者(BufferOutputStream、DataOutputStream等)的增强,OutputStream对象可以被赋予很多强大的功能
在这里插入图片描述
其中,OutputStream就相当于我们的咖啡类,FIleOutputStream相当于我们的具体咖啡类,装饰者(BufferOutputStream、DataOutputStream等)相当于我们的配料类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值