美文网首页
策略模式

策略模式

作者: 长点点 | 来源:发表于2022-04-24 05:49 被阅读0次

本篇语言使用kotlin

为什么使用策略模式

按照个人理解,策略模式可以避免掉大部分switch语句和if esle语句。在之前遇到的项目中,有很多同学在对同一个参数,对应不同处理方式的情形下,多会采用大量的if else,这样的代码也可以运行,但是在后期的维护中,会非常难理解。(其实使用策略模式也会有类膨胀的问题,但也有优化空间,而且个人认为要比上千行的if else好一些)
根据开闭原则,在新增代码逻辑时,优先考虑新增扩展子类,而不是修改原有的类。
在实际项目中,我有遇到过数据库操作的情形,在安卓app中使用room库,需要操作不同的数据表,但是行为都是增删查改,此处可以考虑使用策略模式,横向拓展操作数据表类。以下使用相似的情景代替。

什么是策略模式

面向过程设计

    companion object{
        const val APPLE = 0
        const val ORANGE = 1
        const val GRAPE = 2
    }

    fun eatFruit(fruitType : Int){
        if (fruitType == APPLE){
            pareByKnife()
        }else if (fruitType == ORANGE){
            pareByHand()
        } else if (fruitType == GRAPE){
            withoutPare()
        }
    }
//或者使用
    fun eatFruit(fruitType : Int){
        when (fruitType) {
            APPLE -> {
                pareByKnife()
            }
            ORANGE -> {
                pareByHand()
            }
            GRAPE -> {
                withoutPare()
            }
        }
    }

    fun pareByKnife(){

    }

    fun pareByHand(){

    }

    fun withoutPare(){

    }

在分支数量较少时,采用这种写法是可以接受的。优化一下,使其满足开闭原则。

下面使用策略模式,面向对象设计

class EatFruitContext (eatFruit: EatFruit){
    private var eatFruit : EatFruit = eatFruit

    public fun getWayToEat(){
        eatFruit.howToEat()
    }
}
open class EatFruit {
    public open fun howToEat(){

    }
}
class EatApple : EatFruit()  {
    override fun howToEat() {
        super.howToEat()
        pareByKnife()
    }

    fun pareByKnife(){

    }
}
class EatGrape : EatFruit() {
    override fun howToEat() {
        super.howToEat()
        withoutPare()
    }

    fun withoutPare(){

    }
}
class EatOrange : EatFruit()  {
    override fun howToEat() {
        super.howToEat()
        pareByHand()
    }

    fun pareByHand(){

    }
}
策略模式.png

使用了策略模式之后,虽然满足了开闭原则,但是在几百条不同路径时,会创建非常多的子类,这样会导致类膨胀,反而不利于代码的理解,其实也有优化的思路,(之前遇到过这样的项目,在接收到网络消息后,需要根据协议解析数据,走不同的代码块,协议拓展到上百条后,if else代码会显得非常臃肿,上千行的if else绝对是噩梦级代码,相比于此情形,类膨胀要稍显柔和一些)一种是使用java的函数式接口,另一种是使用反射,反射可以在程序运行时修改属性,是java解释型语言的一种特性。

策略模式的缺陷

优化类膨胀

1.使用函数作为参数的思路

主要思想是,使用HashMap存储下所有的策略,在请求策略时,从map中取出,可以使用字符串或者Int代替下方代码中的EatFruit类的子类对象。

class EatFruitContext (private val eatFruit: EatFruit){
    private val eatApple = EatApple() 
    private val eatOrange = EatOrange()
    private val eatGrape = EatGrape()

    private var functionList : HashMap<EatFruit, () -> Unit> = HashMap()//

    init {
        functionList[eatApple] = { eatApple.howToEat() }
        functionList[eatOrange] = { eatOrange.howToEat() }
        functionList[eatGrape] = { eatGrape.howToEat() }
    }

    public fun getWayToEat() : (() -> Unit)? {
        return functionList[eatFruit]
    }
}
class EatFruitContext (private val eatFruit: Int){
    private val eatApple = 0
    private val eatOrange = 1
    private val eatGrape = 2


    private var functionList : HashMap<Int, () -> Unit> = HashMap()

    init {
        functionList[eatApple] = { //TODO EAT 
            }
        functionList[eatOrange] = { //TODO EAT 
            }
        functionList[eatGrape] = { //TODO EAT
            }
    }

    public fun getWayToEat() : (() -> Unit)? {
        return functionList[eatFruit]
    }
}

java代码可以参考>https://www.csdn.net/tags/Mtjacg4sMDA5MzMtYmxvZwO0O0OO0O0O.html

2.使用反射的思路

结合抽象工厂模式思路会更清晰(之前有个项目就是用反射实例化各个窗口跳转按钮,在配置文件中确定哪些按钮需要实例化)
1.新建xml或者其他类型配置文件,运行时读取配置属性,达到自动实例化对应类的目的(读取xml的ConfigurationManager类在此不贴出来了)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <EatFruitSettings>
        <add key="FruitType" value="apple"/>
        <add key="FruitType" value="orange"/>
        <add key="FruitType" value="grape"/>
    </EatFruitSettings>
</configuration>
class EatFruitContext2 (private val className: String){
    private var functionList : HashMap<String, () -> Unit> = HashMap()

    init {
        for(string in ConfigurationManager.appSetting.keys){
            functionList[string] = {
                (Class.forName(string).kotlin as EatFruit).howToEat()
            }
        }
    }

    public fun getWayToEat() : (() -> Unit)? {
        return functionList[className]
    }
}

2.通过遍历查找到继承EatFruit接口或基类的所有实现类或子类,达到自动实例化对应类的目的
思路和上面类似,遍历实现类或子类的代码就不贴了

策略模式-菜鸟教程

https://m.runoob.com/design-pattern/strategy-pattern.html

相关文章

网友评论

      本文标题:策略模式

      本文链接:https://www.haomeiwen.com/subject/qqetyrtx.html