理解装饰器模式

概念描述

装饰器模式是一种结构型模式,它是对现有对象的一个包装,然后在包装类中加入新的功能。

场景描述

考虑这样一个场景:咖啡店中目前有咖啡,比如espresso,americano,decaf,客人根据自己的喜好,可以选择加糖,加奶或者两者都加,也可以什么都不加,当然,根据加入原料的不同价格也不相同。

设计一:

以直观的想法去设计这样一个结构,应该是


Coffee.java
public class Coffee {
    private String description;
    private double cost;
    public Coffee(String description, double cost) {
        this.description = description;
        this.cost = cost;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public double getCost() {
        return cost;
    }
    public void setCost(double cost) {
        this.cost = cost;
    }
}
Americano.java
public class Americano extends  Coffee{
    public Americano(){
        super("americano",12);
    }
}
SugarAmericano.java
public class SugarAmericano  extends Coffee{

    public SugarAmericano(){
        super("sugarAmericano",15);
    }
}

上面的结构一个很明显的缺陷是,咖啡和配料有很多种组合,需要将每一种组合都定义为一个类,这样的结构会变的庞大而不好维护。

设计二:

第二种改进的方式,可以将配料定义在超类中,在子类中通过条件判断语句,来组成不同的组合

Coffee.java
public class Coffee {
    private String description;
    private double cost;

    private boolean sugar;
    private boolean milk;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public double getCost() {
        return cost;
    }

    public void setCost(double cost) {
        this.cost = cost;
    }

    public boolean isSugar() {
        return sugar;
    }

    public void setSugar(boolean sugar) {
        this.sugar = sugar;
    }

    public boolean isMilk() {
        return milk;
    }

    public void setMilk(boolean milk) {
        this.milk = milk;
    }
}
Americano.java
public class Americano extends Coffee {
    public Americano(){
        super();
    }

    @Override
    public String getDescription() {
        StringBuffer desc= new StringBuffer();
        if(isSugar()){
            desc.append("add sugar,");
        }
        if(isMilk()){
            desc.append("add milk ,");
        }
        desc.append("americano");
       return desc.toString();
    }



    @Override
    public double getCost() {
       double cost= 3.0;
       if(isMilk()){
           cost+=1;
       }
       if(isSugar()){
           cost+=3;
       }

       return cost;
    }
}
TestApp.java
public class TestApp {

    public static void main(String[] args) {
        Coffee coffee = new Americano();
        coffee.setMilk(true);
        System.out.println(coffee.getDescription());
        System.out.println(coffee.getCost());
        coffee.setSugar(true);
        System.out.println(coffee.getDescription());
        System.out.println(coffee.getCost());
    }
}

设计二中的结构比第一种结构简单很多,但是如果新增一种配料,父类和子类都需要做相应的修改,这违反了开放封闭原则。

设计三:

使用装饰器设计模式后,项目结构调整为:



从图中可以看出,CoffeeDecorator是一个抽象装饰器,需要继承和引用抽象主体类。因为装饰器的本体仍然是Coffee

Coffee.java
public abstract class Coffee {
    private String description;
    private double cost;
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

    public  double getCost(){
        return this.cost;
    }

    public void setCost(double cost) {
        this.cost = cost;
    }
}
//具体的主体Americano 
public class Americano extends Coffee {

    public Americano(){
        super();
        super.setCost(3);
        super.setDescription("americano");
    }
}
//具体的主体
public class Espresso extends Coffee {

    public Espresso(){
        super();
        super.setCost(14);
        super.setDescription("espresso");
    }
}
//抽象装饰器
public abstract class CoffeeDecorator extends Coffee {

    private Coffee coffee;

    public CoffeeDecorator(Coffee coffee){
        this.coffee = coffee;
    }

    public double getCost(){
        return super.getCost()+coffee.getCost();
    }

    public String getDescription(){
        return super.getDescription()+","+coffee.getDescription();
    }
}
//具体装饰器
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee){
        super(coffee);
        super.setCost(5);
        super.setDescription("add milk");
    }
}
public class SugarDecorator extends CoffeeDecorator{

    public SugarDecorator(Coffee coffee){
        super(coffee);
        super.setCost(2);
        super.setDescription(" add sugar");

    }


}
//TestApp.java
public class TestApp {
    public static void main(String[] args) {
        //单品
        Coffee americano = new Americano();
        System.out.println("americano:"+americano.getCost());
        System.out.println("americano:"+americano.getDescription());
        //加入糖
        americano = new SugarDecorator(americano);
        System.out.println("sugar americano:"+americano.getCost());
        System.out.println("sugar americano:"+americano.getDescription());
        //再加入牛奶
        americano = new MilkDecorator(americano);
        System.out.println("sugar milk americano:"+americano.getCost());
        System.out.println("sugar milk  americano:"+americano.getDescription());
    }
}

java中的装饰器:

Java中的流是典型的装饰器模式,以输入流为例,FileInputStream是具体的主体,InputStream是抽象主体



同样的具体实体还有ByteArrayInputStream和StringBufferInputStream 。
输入流的抽象装饰器是FilterInputStream ,具体装饰器如BufferedInputStream,DataInputStream等


注意点

软件设计的原则:对修改封闭,对扩展开放
装饰器的优点:可以动态扩展一个对象的功能,比如咖啡Americano单品, 可以加糖,可以加奶,可以两者都加,这个过程,可以在运行时决定如何自由组合。
缺点:多层继承,结构稍复杂。

代码实例在https://github.com/jxl198/designPattern/tree/master/decorator

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容