设计模式系列之四装饰者模式

装饰者模式的定义:

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

Java实现

借用《Head First设计模式》 中的咖啡店卖咖啡的样例。便于说明,这里简洁一下。假如我要写一个咖啡点单计价的软件。业务逻辑是这样的,一杯咖啡10元,如果加糖另外加1元,如果加奶另外加两元。实际来讲,采用硬编码的方式我可以写三个类来实现该需求:1.咖啡单买;2.咖啡+糖;3.咖啡+奶。然后就完事儿,任务完成。但是如果哪一天,老板打算卖即加糖又加奶的咖啡的话,就得再增加一个类来实现该类型咖啡的计价。这还好说,假如我有多种类型的咖啡呢?而且每种类型的咖啡都可以加奶或者糖,这样排列组合下去,我估计要写吐血.....

下面,我们就使用装饰者模式来解决该问题。

样例

首先来一个咖啡的抽象类

public abstract class AbsCoffee {
   //咖啡描述
   String description = "Unknown Type";

   public String getDescription() {
       return description;
   }
   //咖啡加个
   public abstract double cost();
}

接下来两种咖啡的实现

public class MochaCoffee extends AbsCoffee{
   public MochaCoffee() {
       description = "摩卡咖啡";
   }
   @Override
   public double cost() {
       //摩卡咖啡10元一杯
       return 10;
   }
}
public class LatteCoffee extends AbsCoffee{
    public LatteCoffee() {
        description = "拿铁咖啡";
    }
    @Override
    public double cost() {
        //拿铁咖啡15元一杯
        return 15;
    }
}

装饰器

//装饰器
public abstract class Decorator extends AbsCoffee {
    //强制装饰者增加自己的描述信息
    public abstract String getDescription();
}

两个装饰类,这里分别为咖啡的调调:糖、牛奶

public class Sugar extends Decorator{
    AbsCoffee coffee;
    public Sugar(AbsCoffee coffee){
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription()+"+糖";
    }

    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return coffee.cost()+2;
    }
}
public class Milk extends Decorator{
    AbsCoffee coffee;
    public Milk(AbsCoffee coffee){
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription()+"+牛奶";
    }

    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return coffee.cost()+1;
    }
}

测试结果

public class Test {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //摩卡咖啡
        AbsCoffee coffee = new MochaCoffee();
        //打印:摩卡咖啡=10.0
        System.out.println(coffee.getDescription()+"="+coffee.cost());
        //摩卡+糖+牛奶
        coffee = new Milk(new Sugar(coffee));
        //打印:摩卡咖啡+糖+牛奶=13.0
        System.out.println(coffee.getDescription()+"="+coffee.cost());
        /*同理,可以把不同类型的咖啡与不同类型的调料任意组合,解决了硬编码时一种组合就需要对应写一套逻辑的尴尬*/
    }
}

java.io中的装饰者模式

这里的代码样例来自与《Head First设计模式》一书,演示了FileInputStream中装饰者模式的使用

//实现字符的大小写转换
public class LowerCaseInputStream extends FilterInputStream {

    public LowerCaseInputStream(InputStream in) {
        super(in);
    }

    public int read() throws IOException {
        int c = in.read();
        return (c == -1 ? c : Character.toLowerCase((char)c));
    }

    public int read(byte[] b, int offset, int len) throws IOException {
        int result = in.read(b, offset, len);
        for (int i = offset; i < offset+result; i++) {
            b[i] = (byte)Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}
public class InputTest {
    public static void main(String[] args) throws IOException {
        int c;

        try {
            //层层装饰,最终输入大写字符
            InputStream in =
                new LowerCaseInputStream(
                    new BufferedInputStream(
                        new FileInputStream("test.txt")));

            while((c = in.read()) >= 0) {
                System.out.print((char)c);
            }

            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Javascript实现

(function () {
    function MochaCoffee() {
        this.description = "摩卡咖啡"
    }
    MochaCoffee.prototype.cost = function () {
        return 10;
    };
    MochaCoffee.prototype.getDescription = function () {
        return this.description;
    };

    //调料:糖
    function Sugar(coffee) {
        this.coffee         = coffee;
        this.description    = "糖";
    }
    Sugar.prototype.cost = function () {
        return this.coffee.cost()+1;
    };
    Sugar.prototype.getDescription = function () {
        return this.coffee.getDescription()+"+"+this.description
    };
    //调料:牛奶
    function Milk(coffee) {
        this.coffee         = coffee;
        this.description    = "牛奶";
    }
    Milk.prototype.cost = function () {
        return this.coffee.cost()+2;
    };
    Milk.prototype.getDescription = function () {
        return this.coffee.getDescription()+"+"+this.description
    };

    /*************************测试代码*************************/
    var coffee  = new MochaCoffee();
    //这里将打印:摩卡咖啡=10
    console.log(coffee.getDescription()+"="+coffee.cost());
    coffee      = new Milk(new Sugar(coffee));
    //这里将打印:摩卡咖啡+糖+牛奶=13
    console.log(coffee.getDescription()+"="+coffee.cost());
})();

上一篇:设计模式系列之三观察者模式
下一篇:设计模式系列之五工厂模式

你可能感兴趣的:(设计模式系列之四装饰者模式)