Chapter 15 - Exercise 38

(4) Create a simple Decorator system by starting with basic coffee, then providing decorators of steamed milk, foam, chocolate, caramel and whipped cream.


要加深对装饰器模式的理解。适配器也好,装饰器也好,根本的思想是组合代理。我需要添加一些新信息,新功能,在不能多继承的情况下,最朴素的思路就是组合能实现新功能的组件,然后用最简单的代理的方式,实现新功能的接口。适配器模式就是这样做的。

装饰器其实也是这个思路,只不过他既继承基类,又以组合的方式把基类添加进来。所以派生的装饰器类,可以用代理完全照搬基类组件的行为,也可以改变这个行为,也可以添加新的行为。而且由于装饰器继承了基类,所以可以重复装饰。

Exercise38.java

下面的代码,每一层装饰器都会给咖啡里加入一种新配方,并改变它的名字。这是最典型的适用于装饰器的场景。通过套嵌使用一系列不同类型的装饰器,我们最终会得到无数多的新配方咖啡。 下面代码里最重要的部件是CoffeeDecorator类。CoffeeDecorator自己也是一个Coffee,但它内部成员方法全部由内部Coffee组件代理完成。 最后一个CoffeeDecoratorCream装饰器,又添加了一个新函数,可以用奶油泡在咖啡表面画图。



package com.ciaoshen.thinkinjava.newchapter15;
import java.util.*;

public class Exercise38 {
    private enum CoffeeType {
        MOCHA, CAPPUCCINO, LATTE, MOKA;
        public String toString() {
            return name().toLowerCase();
        }
    }
    private static class Coffee {
        private CoffeeType type;
        private String name;
        public Coffee(CoffeeType type) {
            this.type = type;
            name = this.type.toString();
        }
        public CoffeeType getType() {
            return type;
        }
        public String changeName(String prefix) {
            String old = name;
            name = prefix + " " + name;
            return old;
        }
        public void drink() {
            System.out.println(name + " Yammy!");
        }
    }
    /**
     * 注意CoffeeDecorator虽然也是Coffee,但没有继承Coffee基类的成员字段。反而是把所有的操作都转到内部的被装饰组件上。
     * 类似封装了新的一层外部环境。Coffee还是有Coffee的所有函数,但是环境多了新的字段信息,以及新的方法。
     */
    private static class CoffeeDecorator extends Coffee {
        protected Coffee myCoffee;
        public CoffeeDecorator(Coffee c) {
            super(c.getType());
            myCoffee = c;
        }
        public String changeName(String prefix) {
            return myCoffee.changeName(prefix);
        }
        public void drink() {
            myCoffee.drink();
        }
    }
    private static class CoffeeDecoratorMilk extends CoffeeDecorator {
        private static final String NAME = "Milk";
        protected final String INGREDIENT = NAME;
        public CoffeeDecoratorMilk(Coffee c) {
            super(c);
            myCoffee.changeName(INGREDIENT); // 每一层装饰器的构造函数,都给Coffee的NAME字段加个前缀。
        }
    }
    private static class CoffeeDecoratorFoam extends CoffeeDecorator {
        private static final String NAME = "Foam";
        protected final String INGREDIENT = NAME;
        public CoffeeDecoratorFoam(Coffee c) {
            super(c);
            myCoffee.changeName(INGREDIENT);
        }
    }
    private static class CoffeeDecoratorChocolate extends CoffeeDecorator {
        private static final String NAME = "Chocolate";
        protected final String INGREDIENT = NAME;
        public CoffeeDecoratorChocolate(Coffee c) {
            super(c);
            myCoffee.changeName(INGREDIENT);
        }
    }
    private static class CoffeeDecoratorCaramel extends CoffeeDecorator {
        private static final String NAME = "Caramel";
        protected final String INGREDIENT = NAME;
        public CoffeeDecoratorCaramel(Coffee c) {
            super(c);
            myCoffee.changeName(INGREDIENT);
        }
    }
    private static class CoffeeDecoratorCream extends CoffeeDecorator { // only cream decorator can draw
        private static final String NAME = "Cream";
        protected final String INGREDIENT = NAME;
        protected final String SHAPE;
        public CoffeeDecoratorCream(String shape, Coffee c) {
            super(c);
            SHAPE = shape;
            myCoffee.changeName(INGREDIENT);
        }
        public void draw() { // 也可以定义新的方法
            myCoffee.changeName(SHAPE);
        }
    }
    public static void main(String[] args) {
        CoffeeDecoratorCream myCoffee = new CoffeeDecoratorCream("HEART", new CoffeeDecoratorCaramel(new CoffeeDecoratorChocolate(new CoffeeDecoratorFoam(new CoffeeDecoratorMilk(new Coffee(CoffeeType.CAPPUCCINO))))));
        myCoffee.draw();
        myCoffee.drink();
    }
}



Need Java Developers? Hire Me »

I know Java. Talk is cheap, watch my code.

I live in CANADA now.