Kotlin设计模式实现之策略模式

前言

周末抽空看了看《Head First设计模式》,准备专门写一个设计模式的专栏,每一种设计模式写一篇文章来巩固一下,当然,如果能帮到别人,是很开心的事了。

看到题目就知道第一篇写的是策略模式了,下面来详细看看:

正文

首先来说一下需求吧:需要来模拟鸭子,有各种的鸭子,可以游泳,可以叫,有绿颜色的、还有黄颜色的等等。很简单对吧,再加上kotlin的优秀语法,就更简单了:

abstract class Ducks{
    
    fun quack(){
        println("呱呱呱")
    }

    fun swim(){
        println("游啊游")
    }
    
    abstract fun display()
    
}

很简单对吧,一个抽象类,里面有游泳的方法和鸭子叫的方法,还有一个显示的抽象方法,上面所说的绿鸭子和黄鸭子直接继承Ducks,然后在display中进行显示就行了:

class GreenDucks:Ducks(){
    override fun display() {
        println("我是一个绿鸭子")
    }
}

class YellowDucks:Ducks(){
    override fun display() {
        println("我是一个黄鸭子")
    }
}

但是现在来了一个新需求,需要加入飞的方法,需要让鸭子会飞。这不是小菜一碟嘛,直接在抽象类中加一个方法不得了??

fun fly(){
        println("飞飞飞")
    }

第一次优化

上面写的代码有问题,导致整个项目不合逻辑。看起来代码没有问题,咱们只在抽象类中加了一个飞的方法,但是别人继承了鸭子抽象类,写了一个橡胶鸭子,这问题就大了,你能想象一个橡皮鸭子在天上飞啊飞嘛?明显不能,现在需要补救,最简单的方法就是每个实现的子类重写一次父类的飞行方法,如果可以飞在子类中进行实现,如果不可以就不实现。

override fun fly(){
        println("我是橡皮鸭,我不会飞")
    }

第二次优化

第一次优化感觉不太好,父类代码是没动,但是每个子类都需要重写方法,两种鸭子还好说,判断相对简单,二十种、二百种呢?每一次都判断去吗?显然不太现实。那么我们可以想到另一种方法:使用接口:

可以写一个飞的接口,如果子类需要实现,就自行实现进行操作:

interface FlyBehavior {
    fun fly()
}
class GreenDucks:Ducks(),FlyBehavior{
    override fun display() {
        println("我是一个绿鸭子")
    }
    
    override fun fly(){
        println("我会飞")
    }
}

第三次优化

感觉还不如第一次优化呢,第一次优化只是需要不飞行的在子类中重写,但第二次优化就需要判断所有的子类是否实现接口,不行不行,还需要好好优化。

咱们可以把会改变的部分法封装起来,不变的部分不动,这样就算有改变也不怕,随时可以进行修改。

刚才已经写了一个飞行的接口,现在就来继承它:

class FlyWithNoWay: FlyBehavior {
    override fun fly() {
       println("我不会使用翅膀飞行啊,啊啊啊")
    }
}

class FlyWithWings: FlyBehavior {
    override fun fly() {
       println("使用翅膀飞行啊,啦啦啦")
    }
}

还可以把叫的行为也抽成接口:

interface QuackBehavior {
    fun quack()
}

class Squack: QuackBehavior {
    override fun quack() {
        println("我会叫,吱吱吱。。")
    }
}

class Quack: QuackBehavior {
    override fun quack() {
        println("我会叫,嘎嘎嘎")
    }
}

这基本已经大功告成了,我们已经把抽象类中的方法都抽出来了,现在的抽象类是这样:

abstract class Duck() {

    fun swim(){
        println("所有的鸭子都会游泳哦,所以直接写在了抽象类")
    }

    //显示
    abstract fun display()

}

现在就需要给抽象类中放入飞行和叫的接口了,注意:放接口,不放具体实现类,要针对接口编程,不要针对实现编程。

abstract class Duck(var flyBehavior: FlyBehavior? = null, var quackBehavior: QuackBehavior? = null) {

    fun swim(){
        println("所有的鸭子都会游泳哦,所以直接写在了抽象类")
    }

    //执行飞行操作
    fun performFly(){
        flyBehavior?.fly()
    }

    //执行鸭子叫操作
    fun performQuack(){
        quackBehavior?.quack()
    }

    //显示
    abstract fun display()

}

下面来看一个具体鸭子的例子:

class MallardDuck : Duck(flyBehavior = FlyWithWings(),quackBehavior = Quack()) {
    override fun display() {
        println("我是一个真鸭子")
    }
}

很简单,只需要在父类的构造参数中声明需要的具体实现即可。接下来该i到了测试了:

class Test {
    companion object {
        /** 我是main入口函数 **/
        @JvmStatic
        fun main(args: Array<String>) {
            val mallardDuck : Duck = MallardDuck()
            mallardDuck.display()
            mallardDuck.performFly()
            mallardDuck.performQuack()

            val modelDuck : Duck = ModelDuck()
            modelDuck.display()
            modelDuck.performFly()
            modelDuck.performQuack()
            modelDuck.swim()
        }
    }
}

可以看到,两种鸭子都是声明的子类指向了父类,子类实例化的动作不需要在代码中进行硬编码,而是在运行时才指定具体实现的对象。下面来运行下看下实现结果:

结果没问题,成功。

文末总结

策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

欢迎大家关注我的个人公众号,会定期发布安卓、Java学习及搞笑文章。

发布了87 篇原创文章 · 获赞 248 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/haojiagou/article/details/103557623