看到题目就知道第一篇写的是策略模式了,下面来详细看看:
正文
首先来说一下需求吧:需要来模拟鸭子,有各种的鸭子,可以游泳,可以叫,有绿颜色的、还有黄颜色的等等。很简单对吧,再加上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() } } }
可以看到,两种鸭子都是声明的子类指向了父类,子类实例化的动作不需要在代码中进行硬编码,而是在运行时才指定具体实现的对象。下面来运行下看下实现结果:
结果没问题,成功。
文末总结
策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。