一周两个设计模式—设计模式之装饰器模式(第三周)

942 阅读5分钟

业务场景:

上回文说道天庭单身汪小犬使用桥接模式解决了天庭咖啡馆出现的点咖啡选择多的问题,并且顺便帮主人赚了3990两黄金用于和嫦娥仙子聊仙生理想,自己留了10两买狗粮,这不才456两天,天王又来了。

上次说到,天庭咖啡馆只卖一种咖啡伴随着不同的口味,这是咖啡分为大、中、小三个杯的size,但是随着市场经济的发展和竞争对手的增加,例如花果山的那位,居然利用自身的优势果树多,搞了各种种类、各种口味的果汁,这么一来,天庭咖啡馆生意是日渐惨淡了,所以天庭咖啡馆增加了Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡),还都是斥巨资在Amercian引 的,据说被那边的长翅膀的人狠狠阴了一大笔钱,最终的结果是,小犬又有活干了,扩展咖啡种类了。

小犬一看这个需求 easy啊,之前不都是使用桥接模式了,扩展咖啡种类并且添加taste不就可以了吗。 看代码:

class ShortBlack(taste: Taste):Coffee{
     override fun orderCoffee(count: Int) {
        Log.v("=======","${count}杯大杯的${taste.getTaste()}的咖啡")
    }
}

class JuiceTaste:Taste {
    override fun addTaste() {
       Log.v("=========","加牛奶的")
    }

    override fun getTaste():String{
        return "加奶"
    }
}

代码复制一遍就可以了,完工,可以456了。代码提交,主人审核后就可以发布上线了。

纳尼,主人回复了,有问题:如果继续添加新的种类类的数量爆炸了,并且如果添加多分调料、多种组合方式呢。

小犬开始分析问题了:所有的业务都是对于咖啡的具体对象进行一个功能附加,例如大杯、中杯、小杯,咖啡的种类中式、美式、意大利式, 咖啡的味道原味、加糖、牛奶、巧克力、糖+巧克力、牛奶+巧克力等等,PS:每种咖啡的价格是不同的。

等等,主体就是一个咖啡,查看23中设计模式对应的设计模式,装饰者好像挺像的,可以试试,先不打扰主人了。

定义:

动态地将新功能附加到对象上,在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

装饰器角色:

●  抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

●  具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

●  装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

●  具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任

可以尝试了。

抽象构件(Component)角色,里面有描述和价格,抽象了花费,花费的钱由具体实现

    abstract class Coffee {

       var description = "unknown"  //描述属性
          var price = 0.0f  //价格

         abstract fun cost():Float  //费用
      }
    }

具体构件(ConcreteComponent)

    class LongBlack:Coffee() {

        init {
            super.description = "美式咖啡"
        }

        override fun cost(): Float {
            return 3.5f
        }
    }

装饰(Decorator)角色

    open class Decorator(var coffee: Coffee) : Coffee() {

        override fun cost(): Float {
            return BigDecimal.valueOf(super.price.toDouble()).add(BigDecimal.valueOf(coffee.price.toDouble())).toFloat()
        }

    }

具体装饰(ConcreteDecorator)


    class Milk(coffee: Coffee):Decorator(coffee) {
        init {
            super.description = "牛奶口味+${coffee.description}"
            super.price = 0.8f
        }
    }

注意事项:

装饰模式对客户端的透明性要求程序不要声明一个ConcreteComponent类型的变量,而应当声明一个Component类型的变量,对应代码中
需要生成的是Coffee对象而不是具体的类型。纯粹的装饰模式很难找到。装饰模式的用意是在不改变接口的前提下,
增强所考虑的类的性能。在增强性能的时候,往往需要建立新的公开的方法。例如在咖啡系统中,咖啡本身没有甜味并且可能发涩,但是糖是
有甜味的,牛奶是保证丝滑的,所以甜味和丝滑需要新的方法暴露出来。
这就导致了大多数的装饰模式的实现都是“半透明”的,而不是完全透明的。换言之,允许装饰模式改变接口,增加新的方法。
这意味着客户端可以声明ConcreteDecorator类型的变量,从而可以调用ConcreteDecorator类中才有的方法:
例如:加入牛奶后,需要coffee.flow()
半透明的装饰模式是介于装饰模式和适配器模式之间的。适配器模式的用意是改变所考虑的类的接口,也可以通过改写一个或几个方法,
或增加新的方法来增强或改变所考虑的类的功能。大多数的装饰模式实际上是半透明的装饰模式,这样的装饰模式也称做半装饰、半适配器模式。
这个在JAVA I/O库中应用到了 参考《JAVA与模式》之装饰模式

装饰模式的优点:

(1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”
 ,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。

(2)通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

装饰模式的缺点:

由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,
使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

这样就完美解决了。如果以后添加茶、酒什么的,只要重写具体构件,如果需要添加超大杯、蓝莓口味的就重写具体装饰不就完事了,太厉害了 可以把这个模式和主人炫耀一下了。

“主人,请检查一下我写的代码”内心OS“快夸奖我”。又可以愉快的456了。