装饰者模式

跟風遠走 提交于 2020-02-09 05:13:08

装饰者模式定义:

装饰者模式动态的将责任附加到对象上,若要扩展对象,装饰者模式提供了比继承更有弹性的替代方案。

 

装饰者模式类图:

说明:

a)       ConcreteComponent是我们要动态地加上新行为的对象,它扩展自Component;

b)      每个组件都可以单独使用,或者被装饰者包装起来使用;

c)       每个装饰者都“有一个”(包装一个)组件,也就是说,装饰者有一个实例变量以保存某个Component的引用;

d)      Decorator是装饰者共同实现的接口(也可以是抽象类);

e)       ConcreteDecorator有一个实例变量,可以记录所装饰的事物(装饰者包装的Component);

f)       装饰者可以加上新的方法,新行为是通过在旧行为前面或后面做一些计算来添加的。

 

装饰者模式具体示例:

在购买咖啡时,可以在咖啡中加入各种调料,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)或覆盖奶泡。

 

咖啡店会根据所加入的调料收取不同的费用,所以咖啡店订单系统必须考虑到这些调料部分。

 

 

现在考虑用装饰者模式来完成这个订单系统,以实现饮料结算和描述接口。下图是采用装饰者模式的咖啡订单系统类图:

 

说明:

HouseBlend、DarkRoast、Espresso和Decaf是四个具体的组件,每一个组件代表一种咖啡类型;

Milk、Mocha、Soy和Whip同样继承自Beverage,是调料装饰者,它们除了必须实现cost()之外,还必须实现getDescription()。

 

实现代码:

饮料基类:

/**
 * 饮料基类
 */
public abstract class Beverage {
    String description = "Unkown Beverage";
    
    public String getDescription() {
        return description;
    }
    
    public abstract double cost();
}

 

 

调料抽象类CondimentDecorator:

 

/**
 * 调料基类
 */
public abstract class CondimentDecorator extends Beverage {

    public abstract String getDescription();
}

 

具体的饮料:

public class DarkRoast extends Beverage {
    
    public DarkRoast(){ 
        description = "Dark Roast Coffee";
    }

    @Override
    public double cost() {
        return 0.67;
    }

}


public class HouseBlend extends Beverage {
    
    public HouseBlend(){ 
        description = "House Blend Coffee";
    }

    @Override
    public double cost() {
        return 0.89;
    }

}

 

 

 

具体的调料类:

/**
 * Mocha是一个装饰者,所以它扩展自CondimentDecorator;
 * CondimentDecorator继承自Beverage;
 * 
 * 要让Mocha能够引用Beverage,做法如下:
 * 1. 用一个实例变量记录饮料,也就是被装饰者;
 * 2. 想办法让被装饰者(饮料)被记录到实例变量中。
 * 
 * 这里的做法是:把饮料当做构造器的参数,再由构造器将此饮料记录在实例变量中。
 */
public class Mocha extends CondimentDecorator {
    
    Beverage beverage;
    
    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    @Override
    public double cost() {
        return 0.20 + beverage.cost();
    }

}


public class Soy extends CondimentDecorator {
    
    Beverage beverage;
    
    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Soy";
    }

    @Override
    public double cost() {
        return 0.13 + beverage.cost();
    }
}


public class Whip extends CondimentDecorator {
    
    Beverage beverage;
    
    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Whip";
    }

    @Override
    public double cost() {
        return 0.13 + beverage.cost();
    }
}

 

测试代码:

public class Coffee {

    public static void main(String[] args) {
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription()
                + " $ " + beverage.cost());
        
        Beverage beverage2 = new DarkRoast();
        beverage2 = new Mocha(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);
        System.out.println(beverage2.getDescription()
                + " $ " + beverage2.cost());
        
        Beverage beverage3 = new HouseBlend();
        beverage3 = new Soy(beverage3);
        beverage3 = new Mocha(beverage3);
        beverage3 = new Whip(beverage3);
        System.out.println(beverage3.getDescription()
                + " $ " + beverage3.cost());
    }

}

 

Java I/0类库是使用装饰者模式最典型的例子,请看下面的类图:

 

 但是Java I/0也引出了装饰者模式的一个“缺点”:利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,

 可能会造成使用此API程序员的困扰。现在已经了解了装饰者模式的工作原理,以后当使用别人的大量装饰的API时,

 就可以很容易的辨别出他们的装饰者类是如何组织的,以方便用包装方式取得想要的行为。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!