行为型模式
开闭原则:策略模式
设想一个游泳的场景,David学习有用,很快掌握了蛙泳、仰泳、自由泳多种姿势。我们封装David 三种不同的有用姿势。
class Swimmer {
fun breakstroke() {
println("I am breakstroking...")
}
fun backstroke() {
println("I am backstrock...")
}
fun freestyle() {
println("I am freestyling...")
}
}
- 并不是所有运动者都掌握三种游泳。Swimmer对象可以调用所有方法,不合适。
- 后续新的游泳姿势加入,需要修改Swimmer,违背开闭原则。
修改:将这些行为分开封装成接口,不同场景调用不同的接口。策略模式就是解决这种场景的很好思路。
策略模式定义了算法族,分别封装起来i,让他们可以互相替换,这个模式让算法的变化,独立于使用算法的客户。
也就是说,策略模式,将不同行为的策略进行独立封装,与类在逻辑上解耦。根据不同的上下文,用类对象进行调用。
普通常用的方式:
interface SwimStrategy {
fun swim()
}
class BreakStroke : SwimStrategy {
override fun swim() {
println("I am breakstroking...")
}
}
class BackStroke : SwimStrategy {
override fun swim() {
println("I am backstroke...")
}
}
class Freestyle : SwimStrategy {
override fun swim() {
println("I am freestyling...")
}
}
class Swimmer(val strategy: SwimStrategy) {
fun swim(){
strategy.swim()
}
}
fun main() {
val weekendDavid = Swimmer(Freestyle())
weekendDavid.swim()
val weekdaysDavid = Swimmer(BreakStroke())
weekdaysDavid.swim()
}
实现了解耦和复用,在不同场景切换采用不同的策略。但是打码量变大。我们继续优化
高阶函数抽象算法
重新思考一下kotlin高阶函数,我们将策略封装成函数而不是类,然后作为参数传入Swimmer类,会更加简洁。因为策略类的目的很明确,就是针对行为算法的抽象,所以高阶函数是一个很好的替代思路。
fun breaststroke(){ println("I am breakstroking...")}
fun backstoke(){println("I am backstroke...")}
fun freestyle(){ println("I am freestyling...")}
class Swimmer2(val swimming:()->Unit){
fun swim(){
swimming()
}
}
>>>
fun main() {
val weekendLili = Swimmer2(::freestyle)
weekendLili.swim()
val weekdaysLili = Swimmer2(::breaststroke)
weekdaysLili.swim()
}
代码量非常少,结构容易阅读。
策略算法封装成一个个函数,初始化Swimmer对象的时候,可以用函数引用语法传递构造参数。
我们也可以把val声明成Lambda方式,再传参时候会更加直观。
模板方法模式:高阶函数代替继承
某种程度上,模板方法和策略模式要解决的问题是相似的,他们都可以分离通用算法和具体上下文。策略模式采用的是将算法进行委托,那么传统模板方法模式更多是基于继承的方式的实现。
模板方法指导思想:
定义一个算法中的操作框架,而将一些步骤延迟到子类中,使得子类在不改变算法框架结构即可重新定义该算法的某些特定步骤。
与策略不同,模板行为算法有更清晰的大纲结构,完全相同的步骤会在抽象类中实现,个性化实现会在子类中实现。
举个栗子:
去办理业务
1)排队取号等待
2)根据自己需求办理个性化服务,社保,户口,房产证等
3)对服务人员进行评价
abstract class CivicCenterTask{
fun execute(){
this.lineUp()
this.askForHelp()
this.evaluate()
}
private fun lineUp(){
println("line up to take a number")
}
private fun evaluate(){
println("evaluaten service attitude.")
}
abstract fun askForHelp()
}
class PullSocialSecurity:CivicCenterTask(){
override fun askForHelp() {
println("ask for pulling the social security.")
}
}
class ApplyForCitizenCard:CivicCenterTask(){
override fun askForHelp() {
println("apply for a citizen card")
}
}
fun main() {
val pass = PullSocialSecurity()
pass.execute()
val afcc = ApplyForCitizenCard()
afcc.execute()
}
Kotlin 同样可以用改造策略模式的的类似思路,简化,依靠高阶函数,不用abstract,传入高阶函数,个性化类用函数替代。
class CivicCenterTask2 {
fun execute(askForHelp:()->Unit) {
lineUp()
askForHelp()
evaluate()
}
private fun lineUp() {
println("line up to take a number")
}
private fun evaluate() {
println("evaluaten service attitude.")
}
}
fun pullSocialSecurity() {
println("ask for pulling the social security.")
}
fun applyForCitizenCard() {
println("apply for a citizen card")
}
》》
val task1 = CivicCenterTask2()
task1.execute(::pullSocialSecurity)
val task2 = CivicCenterTask2()
task2.execute (::applyForCitizenCard)
// 其实都不用在创建task2,复用1就好
task1.execute(::applyForCitizenCard)
总结下,从上面的例子来看,kotlin用高阶函数从表现形式和代码量可以更好的实现