策略模式(Strategy Pattern)

2023-02-04  本文已影响0人  筱湮

说明:本文为《设计模式之禅》的阅读笔记,主要总结精华和记录自己的部分理解。代码部分由Kotlin实现。

1. 定义

策略模式(Strategy Pattern)也叫政策模式(Policy Pattern)。

Define a family of algorithms,encapsulate each one,and make them interchangeable.
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

通俗理解:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的通用类图如下:


策略模式的通用类图.png

其中,有三个角色:

策略模式的通用源码如下:
代码清单1 策略模式通用代码

// 抽象策略
interface Strategy {
    // 策略模式的运算法则
    fun doSomething()
}

// 具体策略
class ConcreteStrategy1: Strategy {
    override fun doSomething() {
        println("具体策略ConcreteStrategy1 的运算法则")
    }
}

// 具体策略
class ConcreteStrategy2: Strategy {
    override fun doSomething() {
        println("具体策略ConcreteStrategy2 的运算法则")
    }
}

// 封装角色
// 构造函数设置具体策略
class Context(private val strategy: Strategy) {

    // 封装后的策略方法
    fun doAnything() {
        strategy.doSomething()
    }
}

// 场景类
fun main(args: Array<String>) {
    // 声明一个具体的策略
    val strategy: Strategy = ConcreteStrategy1()
    // 声明上下文对象
    val context = Context(strategy)
    //执行封装后的方法
    context.doAnything()
}

运行结果:

具体策略ConcreteStrategy1 的运算法则

Process finished with exit code 0

策略模式的重点就是封装角色,它是借用了代理模式的思路。那它和代理模式有什么差别?差别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口那就成为了代理模式。

再来看一个具体的实例。
购买电影票时,提供了多种折扣方式,根据折扣类型进行打折。使用策略模式来实现。

类图如下:


电影票打折类图.png

代码清单2 电影票打折

// 抽象策略类-折扣
interface Discount {
    fun calculate(price: Float): Float
}

// 具体策略类-儿童折扣
class ChildrenDiscount : Discount {
    override fun calculate(price: Float): Float {
        return price - 10
    }
}

// 具体策略类-学生折扣
class StudentDiscount: Discount {
    override fun calculate(price: Float): Float {
        return price * 0.9F
    }
}

// 具体策略类-Vip折扣
class VipDiscount : Discount {
    override fun calculate(price: Float): Float {
        return price * 0.7F
    }
}

// 封装类
class MovieTicket(private val discount: Discount) {

    var originalPrice: Float = 0F

    // 封装后的策略方法
    fun getPrice(): Float {
        return discount.calculate(originalPrice)
    }
}

// 场景类
fun main(args: Array<String>) {

    // ----------------------------- movie ticket -----------------------------
    val discount: Discount = VipDiscount()
    val movieTicket = MovieTicket(discount)
    movieTicket.originalPrice = 60F
    val currentPrice = movieTicket.getPrice()
    println("票价为:$currentPrice,折扣类型:${discount.javaClass.simpleName}")
}

运行结果:

票价为:42.0,折扣类型:VipDiscount

Process finished with exit code 0

2. 策略模式的优缺点

2.1 优点

2.2 缺点

策略模式特点总结

  1. 使用策略模式可以避免使用多重条件语句,如 if…else 语句、switch…case 语句;
  2. 提供了一系列的可供重用的策略实现,适合使用继承可以把公共代码转移到父类里面,从而避免重复的代码;
  3. 提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新策略实现;
  4. 策略模式会新增很多的策略类,增加了维护难度

3. 使用场景

策略模式的注意事项

如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会成为一个烫手山芋,谁都不想接。

4. 最佳实践

策略模式在项目中使用得非常多,但它单独使用的地方就比较少,因为它有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一个策略

附1:思维导图


策略模式 (Strategy Pattern).png

附2:代码实现 https://github.com/ooxiaoyan/StrategyPattern

上一篇 下一篇

猜你喜欢

热点阅读