java常用设计模式,四装饰者模式

  设计模式第四篇,装饰者模式,大家多多指教。

  装饰者模式是动态的将责任附加到对象上(引自《Head First设计模式》)。这里的重点在于动态这两个字,我们都知道继承的实现的方式,它是是类编译的时候就去加载文件,属于一种静态的附加,而我们要实现动态的附加就不能单纯的通过继承来实现。在这种背景下,装饰者模式就应运而生了。装饰者模式的实现:首先所有的类都有一个共同的抽象,这个抽象可以是一个抽象类,也可以是一个接口,所有的类该抽象的子类或者实现。语言描述比较抽象,下面我们通过一个例子来描述该模式。

 例子

  我们举一个吃火锅的例子,根据装饰者模式的特点:

  首先我们要有个共同的抽象,这里我们通过接口来实现,这个接口我们称之为锅底。  

/*
 * 锅底
 */
public interface Hotpot {
    /** 描述 */
    String getName();
    /** 价格 */    
    Double getPrice();

}

  第二步,我们设计具体的锅底类,我们分为酱香锅、香辣锅和清汤锅,这是底锅,每笔单都必须有且只有一个。

public class JamHotpot implements Hotpot{
    @Override
    public String getName() {
        return "酱香锅";
    }

    @Override
    public Double getPrice() {
        return 4.0;
    }
}

public class SpicyHotpot implements Hotpot{
    @Override
    public String getName() {
        return "香辣锅";
    }

    @Override
    public Double getPrice() {
        return 4.0;
    }
}

public class ClearSoupHotpot implements Hotpot{
    @Override
    public String getName() {
        return "清汤锅";
    }

    @Override
    public Double getPrice() {
        return 3.0;
    }
}

  第三步,我们新增一个配菜接口,继承共同的接口锅底,这个接口用来作为所有配菜的父级,该接口可以拓展所以配菜的共有属性

/*
 * 配菜
 */
public interface SideDish extends Hotpot {
    //可拓展配菜的公共属性
}

  第四步,我们需要新增具体的配菜类,这里的配菜类我们注意到比锅底类多了成员变量和构造方法,而且getName和getPrice方法多了加了传入参数,这个就是为了动态的去附加功能,具体怎么实现参考最后一步。

/*
 * 牛肉
 */
public class Beef implements SideDish {

    private Hotpot hotpot;

    public Beef(Hotpot hotpot) {
        this.hotpot = hotpot;
    }

    @Override
    public String getName() {
        return hotpot.getName() + ",牛肉";
    }

    @Override
    public Double getPrice() {
        return hotpot.getPrice() + 38.0;
    }
}

/*
 * 生菜
 */
public class Lettuce implements SideDish {

    private Hotpot hotpot;

    public Lettuce(Hotpot hotpot) {
        this.hotpot = hotpot;
    }

    @Override
    public String getName() {
        return hotpot.getName() + ",生菜";
    }

    @Override
    public Double getPrice() {
        return hotpot.getPrice() + 8.0;
    }
}

  最后我们测试:

public class DecoratorTest {

    public static void main(String[] args) {
        System.out.println("=======酱香锅底加牛肉");
        Hotpot hotpot = new JamHotpot();
        hotpot = new Beef(hotpot);
        hotpot = new Lettuce(hotpot);
        System.out.println("===火锅的价格为:" + hotpot.getPrice() + "元");
        //如果我们需要更多的配菜参考上面的方式去添加更多的配菜  
    }
}

 总结

  如上面所描写的那样,我只需要前台传递给我们所点的锅底和配菜,我们后台去动态的生成一个新的火锅对象。这么设计的好处的拓展极其方便,就算这个火锅店加了新的锅底,或者是配菜,对我们之前的代码是没有任何影响的,这就是所谓的开闭原则(对拓展开放,对修改关闭),后面我们会讲到设计模式相应的原则。除了上面的好处之外,装饰者模式也有他本身不能忽视的问题,那就是每次生成一笔订单都会生成很多小对象,如果该笔单对象很多的话,对我们的内存是个不小的考验,而且过多的小类会让我们的系统变得很复杂,让人很难以理解。所以装饰器模式有利有弊(更多的是利大于弊),我们可以好好思考装饰者模式应用场景,然后进一步掌握这种设计思想。

 番外

  想了解装饰者模式的更多详细信息可以通过jdk源码或者spring源码去了解。jdk中IO操作就有通过装饰者模式实现的,FilterInputStream和它的实现就是一个装饰者模式,同样的在spring源码中,凡事以Decorator结尾的类都是实现的装饰者模式,当然不是所有的装饰器模式都是以Decorator结尾的。