0
点赞
收藏
分享

微信扫一扫

一文看懂装饰者模式

学习经典的设计模式的时候装饰者模式真是让人头疼,但是掌握了其基本思想后并不难实现。

首先“装饰者”已经告诉了我们两个基本信息

  1. 要有东西被装饰,可以看作一个核心
  2. 要有装饰的东西,可以看作不同的属性或功能

请添加图片描述

很简单吧,就两个信息足以让我们写出全部的模板代码。装饰者模式要做的,就是把不同的功能一层一层地包到核心的外面而已。

首先设计模式看多了就知道,“核心类”和“装饰类”必然是分为抽象的和具体的类。在此次例子中,咖啡就是我们的核心,需要添加的配料就是我们的装饰。
但是首先来看最终的客户端调用代码:

public static void main(String[] args) {
        //下单一杯咖啡Espresso(意大利咖啡)---核心
        Order order = new Espresso();
        //加一点牛奶  ---装饰
        order = new Milk(order);
        //加一点巧克力 ---装饰
        order = new Chocolate(order);

        System.out.println("一共"+order.cost()+"元:" + order.getDsc());
    }

打印结果:
在这里插入图片描述
怎么样,是不是很符和我们的预料,那么接下来就来看看如何实现它!

从客户端代码可知,这个Order类从头用到尾,既接收了核心实现类Espresso又接收了装饰者实现类Milk和Chocolate。
那么可以获取结论一,抽象核心类和抽象装饰类都是这个Order类的子类!

既然Order类是最顶层的父类,那么它的属性必然是其子类都具有的,这里我们就将这些属性确认为价格描述/名称(咖啡和配料都有单独的价格和名称)

给出Order类的代码:

public abstract class Order {
    public String dsc;//描述
    private long price = 0;//价格

    public String getDsc() {
        return dsc;
    }

    public void setDsc(String dsc) {
        this.dsc = dsc;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    public abstract long cost();
}

可以发现它除了两个属性的getter和setter方法外,留了个cost方法(获取此次订单总价)给子类实现。

接下来就可以把核心类Espresso类写出来了,当然咖啡不只有一种,所以我们为了扩展性,别怕麻烦给Espresso也确定一个父类,即Coffee咖啡类

Coffee类代码:

public class Coffee extends Order {
    //获取订单价格
    @Override
    public long cost() {
        return super.getPrice();
    }
}

可以发现Coffee类没干什么事,只是返回Order订单类的价格而已。

Espresso的代码:

public class Espresso extends Coffee{
    public Espresso(){
        setDsc("意大利咖啡");
        setPrice(10);
    }
}

可知这个类调用了父类的方法,给父类两个基本属性赋了值,先来看看继承链。
在这里插入图片描述
再来回顾客户端第一行代码

public static void main(String[] args) {
        //核心咖啡Espresso(意大利咖啡)
        Order order = new Espresso();
        //装饰者牛奶
        order = new Milk(order);
        //装饰者巧克力
        order = new Chocolate(order);

        System.out.println("一共"+order.cost()+"元:" + order.getDsc());
    }

就可以发现此时order的dsc和price属性已经分别被赋上了“意大利咖啡”和“10”。

再来看一看第二行代码,装饰类的构造参数是核心类,这点很好理解。要装饰一个对象那我先得持有这个对象吧。
而由结论一可知,核心类装饰类统统是继承自Order类,所以抽象装饰类的构造参数就是它的父类Order类,这就是结论二

至此,核心咖啡我们已经有了,剩下的就是添加配料了,同上,我们也分别写出装饰抽象类和装饰实现类Milk,Chocolate。

装饰抽象类代码:

public abstract class Decorator extends Order {

    private Order drink;

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

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

    //调味的描述+咖啡的描述
    @Override
    public String getDsc(){
        return super.getDsc() + "/"  + drink.getDsc();
    }
}

这里覆盖了cost方法,将此次订单的价格变成了装饰者配料的价格 + 核心类咖啡的价格。也覆盖了订单的名称,在原来的描述上加上了装饰者的名称。

由客户端代码和结论一可知这里的super.getXXX获取的是装饰者实现类的属性

装饰者实现类的代码:

public class Milk extends Decorator {
    public Milk(Order obj) {
        super(obj);
        setDsc("牛奶");
        setPrice(2);
    }
}

public class Chocolate extends Decorator {
    public Chocolate(Order obj) {
        super(obj);
        setDsc("巧克力");
        setPrice(1);
    }
}

装饰者实现类的继承链是这样的
在这里插入图片描述
和核心类的继承链是不是非常类似?最后我们多加几个咖啡子类和配料子类,整个关系图就变得非常对称了
在这里插入图片描述
记住最重要的结论

  1. 装饰者和被装饰者都继承自一个父类
  2. 装饰者类里面聚合了被装饰者
  3. 装饰者类需要重写父类方法,基于装饰者基础功能上去实现业务逻辑。这样每包一层装饰者,功能就会得到对应的提升

再再来看一遍客户端代码,是不是感觉装饰者模式既简单又精妙呢?!

public static void main(String[] args) {
        //核心咖啡Espresso(意大利咖啡)
        Order order = new Espresso();
        //装饰者牛奶
        order = new Milk(order);
        //装饰者巧克力
        order = new Chocolate(order);

        System.out.println("一共"+order.cost()+"元:" + order.getDsc());
    }

Java的IO包下的代码就是装饰者模式的极致运用
举个例子:

  • InputStream是抽象类Order
  • FileInputStream和其他是核心子类
  • FilterInputStream是Decorator,其中包含有被装饰者
  • DataInputStream是Decorator子类

具体实现可以自己慢慢去品味~

举报

相关推荐

0 条评论