Kotlin设计模式实现之装饰者模式(Decorator)

你离开我真会死。 提交于 2020-01-02 09:39:59

前言       

今天是2020年的第一天,在这里祝大家元旦快乐!之前用kotlin实现了策略模式,文中写到要多写几篇文章来加深以下对设计模式的理解。那么今天要写的看题目应该就知道了:装饰者模式(也叫装饰模式)。下面是装饰者模式的定义:

装饰者模式(Decorator):在不改变对象自身的基础上,动态地给一个对象添加一些额外的职责。与继承相比,装饰者是一种更轻便灵活的做法。若要扩展功能,装饰者提供了比继承更有弹性的替代方法。

故事场景

小星刚毕业,到一家公司实习。今天来到公司后,一如既往地开始编写它的增删改查。

刚刚坐下打开电脑,技术锦鲤走了过来,小星内心开始发牢骚(锦鲤来干啥,每回它来都没好事)。锦鲤告诉小星,公司想要编写一个卖咖啡的系统,有不同种类的咖啡,需要能计算出咖啡的钱和区分咖啡的类别。

小星:没问题,很简单。

十分钟后,小星写出了它的第一版代码:

咖啡的基类:

abstract class Beverage(var description: String = "Unknown Beverage") {
    
    //描述
    open fun getDescriptions():String{
        return description
    }

    //价钱
    abstract fun cost():Double

}

其他咖啡(子类):

/**
 * 深度烘焙咖啡(星巴克)
 *
 * @author jiang zhu on 2020/1/1
 */
class DarkRoast:Beverage() {

    init {
        description = "DarkRoast"
    }

    override fun cost(): Double {
        return 2.99
    }
}

/**
 * 意式浓缩咖啡(星巴克)
 *
 * @author jiang zhu on 2019/12/16
 */
class Espresso:Beverage() {

    init {
        description = "Espresso"
    }

    override fun cost(): Double {
        return 1.99
    }
}

技术锦鲤:你这个卖咖啡看着有问题啊。

小星:没问题啊,您看啊,咱们有一个Beverage的抽象类,里面有getDescription()记录咖啡的描述,还有一个cost()的抽象方法来让子类计算价格。逻辑没问题啊,如果你想增加一种咖啡的话,直接增加子类不就好了吗?

技术锦鲤:但是之前没有考虑到一个问题,咖啡可以加入各种调料,比如:蒸奶、豆浆、摩卡,但这些公司的系统就无能为力了。

小星:也可以啊,比如你有DarkRoast加牛奶,你只需建一个MilkDarkRoast类继承Beverage就可以了,还比如你有DarkRoast加摩卡,你只需再建一个MochaDarkRoast类继承Beverage就可以了啊。

技术锦鲤:对,是可以这样来实现,但是你想过吗?如果咖啡种类少了还行,要是有很多呢?你好好想想。

小星在脑海中想出了下面这幅图:

小星:对哦,这样的话类就太多了,而且很多重复的东西,那应该怎么解决呢?

技术锦鲤:你可以换个思路,既然继承无法解决这个问题,那么可以采用不一样的做法:我们可以将Beverage作为主体,然后在运行时以调料(蒸奶、豆浆、摩卡等)来装饰Beverage,就像你刚才举的例子:我们只需拿一个DarkRoast对象,然后来选择用牛奶或摩卡来装饰(Decorate)就可以了。

小星:我听得有点懵,您能具体说说吗?

技术锦鲤:你可以创建一个CondimentDecrator的抽象类来继承Beverage,然后你其他所有的调料就都可以抽出来继承CondimentDecrator,这样你只需要选择好咖啡,然后再选择你需要的调料就行了,而且还可以添加多种调料。

下面是技术锦鲤给的图:

小星:我好像有点明白了。

技术锦鲤:那你看看修改以下你的代码吧。

小星听从锦鲤的指导,刚才建的咖啡类没做改动,新增加了一个CondimentDecrator类:

abstract class CondimentDecorator:Beverage() {

    abstract override fun getDescriptions():String

}

将摩卡、蒸奶等调料建立类继承自CondimentDecrator:(注意在cost方法中要加上咖啡的价格,描述时也要加上咖啡的名称)

//摩卡
class Mocha(var beverage: Beverage):CondimentDecorator() {
    override fun getDescriptions(): String {
        return "${beverage.getDescriptions()},Mocha"
    }

    override fun cost(): Double {
        return 0.20+beverage.cost()
    }
}
//奶油
class Whip(var beverage: Beverage):CondimentDecorator() {
    override fun getDescriptions(): String {
        return "${beverage.getDescriptions()},Whip"
    }

    override fun cost(): Double {
        return 0.10+beverage.cost()
    }
}

小星:终于写完了,但是还不知道能不能成功。

技术锦鲤:你可以测试一下啊,来,我看着你测试。

小星在锦鲤的注视下写出了以下测试方法:

class Test {
    companion object {
        /** 我是main入口函数 **/
        @JvmStatic
        fun main(args: Array<String>) {
            var beverage2 : Beverage = DarkRoast()
            beverage2 = Mocha(beverage2)
            beverage2 = Mocha(beverage2)
            beverage2 = Whip(beverage2)
            println(beverage2.getDescriptions()+beverage2.cost())
        }
    }
}

技术锦鲤:点击运行吧,看看是不是DarkRoast中加了两份摩卡和一份奶油,还有就是价钱看看算的对不对哦。

小星忐忑地点了运行按钮:

技术锦鲤:不错不错,小星写出来了,你已经掌握了装饰者模式,“在不改变对象自身的基础上,动态地给一个对象添加一些额外的职责。”你已经做到了。但是有点小问题,你的价格出来的结果可不太对,虽然可以忽略不记,但是写程序一定要严谨。给你点提示吧,你可以去查一下BigDecimal这个类,你就知道你的计算精度问题了。

小星:好嘞,谢谢锦鲤。

小星很开心,学习了一种设计模式,感觉很巧妙,好像还有二十二种,啊。。。。慢慢来吧。

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