《kotlin核心编程》阅读笔记 第九章 设计模式

第九章 设计模式

基于Kotlin崭新的语言特性,实现或替换了Java中部分典型设计模式。
GoF针对常见设计模式分类:创建型模式、行为型模式、结构型模式。

创建型模式

创建怎样的对象,如何且合适创建它们,以及对类和对象的配置,都是实际代码中编写中需要考虑的问题。

伴生对象增强工厂模式

工厂模式可以细分为简单工厂、工厂方法模式以及抽象工厂。它的核心作用就是通过一个工厂类隐藏对象实例的创建逻辑,而不需要暴露给客户端。典型的使用场景就是当拥有一个父类与多个子类的时候,我们可以通过这种模式来创建子类对象。
示例:需求为现在有一个电脑加工厂,同时生产个人电脑和服务器主机。

interface Computer{
    val cpu:String
}

class PC(override val cpu:String = "Core"):Computer
class Server(override val cpu: String="Xeon"):Computer

enum class ComputerType{
    PC,Server
}
class ComputerFactory {
    fun produce(type: ComputerType):Computer{
        return when(type){
            ComputerType.PC -> PC()
            ComputerType.Server -> Server()
        }
    }
}
fun main() {
    val comp = ComputerFactory().produce(ComputerType.PC)
    println(comp.cpu)
}
>>>
Core

上面的代码在不同的地方使用时都需要创建工厂类,可以将工厂类做成单例。

使用单例代替工厂类

Kotlin支持用object来实现Java中的单例模式。

object ComputerFactory {
    fun produce(type: ComputerType):Computer{
        return when(type){
            ComputerType.PC -> PC()
            ComputerType.Server -> Server()
        }
    }
}
fun main() {
    val comp = ComputerFactory.produce(ComputerType.PC)
    println(comp.cpu)
}

只需要修改一下即可。
Kotlin支持运算符重载,可以通过operator 操作符重载invoke 方法来代替 produce ,从而进一步简化;

object ComputerFactory {
    operator fun invoke(type: ComputerType):Computer{
        return when(type){
            ComputerType.PC -> PC()
            ComputerType.Server -> Server()
        }
    }
}
fun main() {
    val comp = ComputerFactory(ComputerType.PC)
    println(comp.cpu)
}
伴生对象创建静态工厂方法

考虑用静态工厂方法代替构造器–《Effective Java》.

class PC(override val cpu:String = "Core"):Computer
class Server(override val cpu: String="Xeon"):Computer

enum class ComputerType{
    PC,Server
}

interface Computer{
    val cpu:String
    companion object{
        operator fun invoke(type:ComputerType):Computer{
            return when(type){
                ComputerType.PC -> PC()
                ComputerType.Server -> Server()
            }
        }
    }
}

fun main() {
    val comp = Computer(ComputerType.PC)
    println(comp.cpu)
}

用Factory 命名 Computer的伴生对象。

interface Computer{
    val cpu:String
    companion object Factory{
        operator fun invoke(type:ComputerType):Computer{
            return when(type){
                ComputerType.PC -> PC()
                ComputerType.Server -> Server()
            }
        }
    }
}

fun main() {
    val comp = Computer(ComputerType.PC)
    println(comp.cpu)
    val comp1 = Computer.Factory(ComputerType.Server)
    println(comp1.cpu)
}
扩展伴生对象方法

假设业务中我们是Computer 接口的使用者,比如它是工程引入的第三方类库,所有的类的实现都得到了很好的隐藏。那么、如果我们希望进一步改造其中的逻辑,Kotlin的伴生对象的方式同样依靠其扩展函数的特性,很好的实现这一需求。
示例:给Computer 增加一种功能,通过CPU型号来判断电脑类型。


fun Computer.Factory.fromCPU(cpu: String):ComputerType? = when(cpu){
    "Core" -> ComputerType.PC
    "Xeon" ->ComputerType.Server
    else -> null
}

fun main() {
    val comp = Computer(ComputerType.PC)
    println(Computer.Factory.fromCPU(comp.cpu))
}
内联函数简化抽象工厂

内联函数 可以 具体化参数类型。
示例:工厂模式已经能够很好地处理一个产品等级结构的问题,在上面我们已经用它很好的解决了电脑厂商生成服务器,PC的问题。进一步考虑,当问题上升到多个产品等级结构的时候,比如现在引入了品牌商的概念,有好几个不同的电脑品牌,Dell、Asus、Hasee,那么有必要再增加一个工厂类。然而,我们并不希望对每个模型都建立一个工厂,这样会让代码变得难以维护,所以这时候我们需要引入抽象工厂模式。

抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

interface Computer
class Dell:Computer
class Asus:Computer
class Hasee:Computer

class DellFactory:AbstractFactory(){
    override fun produce()  = Dell()
}

class AsusFactory:AbstractFactory(){
    override fun produce()  = Asus()
}

class HaseeFactory:AbstractFactory(){
    override fun produce()  =  Hasee()
}

abstract class AbstractFactory {
    abstract fun produce():Computer
    companion object{
        operator fun invoke(factory: AbstractFactory):AbstractFactory{
            return factory
        }
    }
}

fun main() {
    val dellFactory = AbstractFactory(DellFactory())
    val dell = dellFactory.produce()
    println(dell)
}
interface Computer
class Dell : Computer
class Asus : Computer
class Hasee : Computer

class DellFactory : AbstractFactory() {
    override fun produce() = Dell()
}

class AsusFactory : AbstractFactory() {
    override fun produce() = Asus()
}

class HaseeFactory : AbstractFactory() {
    override fun produce() = Hasee()
}

abstract class AbstractFactory {
    abstract fun produce(): Computer

    //    companion object{
//        operator fun invoke(factory: AbstractFactory):AbstractFactory{
//            return factory
//        }
//    }
    companion object {
        inline operator fun <reified T : Computer> invoke(): AbstractFactory =
                when (T::class) {
                    Dell::class -> DellFactory()
                    Asus::class -> AsusFactory()
                    Hasee::class -> HaseeFactory()
                    else -> throw IllegalArgumentException()
                }
    }
}

fun main() {
    val dellFactory = AbstractFactory<Dell>()
    val dell = dellFactory.produce()
    println(dell)
}

通过将invoke 方法用 inline 定义为内联函数。就可以引入reified 关键字,使用具体化参数类型的语法特性;
要具体化参数类型为Computer。在invoke方法中判断它的具体类型,来返回对应的工厂类对象。

用具名可选参数而不是构建者模式

使用Builder(构建者)模式:构建者模式和单例一样,也是GoF设计模式中的一种。他主要做的是事情就是将一个复杂对象的构建与它的表示分离,使得同样的构建过程中可以创建不同的表示。
工厂模式和构造函数都存在相同的问题,就是不能很好地扩展到大量的可选参数。假设我们现在有个机器人类,它包含多个属性:代号、名字、电池、重量、高度、速度、音量等。很多产品都不具有其中的某些属性,比如不能走,不能发声。甚至有点机器人不需要电池。

class Robot private constructor(
        val code:String,
        val battery:String?,
        val height:Int?,
        val weight:Int?
){
    class Builder(val code:String){
        private var battery:String? = null
        private var height:Int? = null
        private var weight:Int? = null
        fun setBattery(battery: String?):Builder{
            this.battery = battery
            return this
        }

        fun setHeight(height: Int?):Builder{
            this.height = height
            return this
        }
        fun setWeight(weight: Int?):Builder{
            this.weight = weight
            return this
        }
        fun build():Robot{
            return Robot(code,battery, height, weight)
        }
    }

    override fun toString(): String {
        return "robot[code = $code,battery=$battery,height = $height,weight = $weight]"
    }
}

fun main() {
    val robot = Robot.Builder("001")
            .setBattery("KK")
            .setHeight(50)
            .setWeight(30)
            .build()
    println(robot)
}
>>>robot[code = 001,battery=KK,height = 50,weight = 30]

实现思路:

  1. Robot 类内部定义了一个嵌套类Builder,由他负责创建Robot 对象。
  2. Robot 类的构造函数用private进行修饰,这样可以确保使用者无法直接通过Robot 声明实例。
  3. 通过Builder类定义set方法来对可选的属性进行设置。
  4. 最终调用Builder类中的build 方法来返回一个Robot对象。

构建者模式存在的一些不足:

  1. 如果业务需求的参数很多,代码依然会显得比较冗长。
  2. 你可能会在使用Builder的时候忘记在最后调用build 方法;
  3. 由于在创建对象的时候,必须先创建他的构造器,因此额外增加了多余的开销,在某些十分注重性能的情况下,可能就会存在一定的问题。
    本质上builder模式模拟了具名的可选参数,就像Ada和Python中一样。Kotlin也是这样一门拥有具名可选参数的编程语言。
具名的可选参数

表现为两点:在具体化一个参数的取值时,可以通过带上它的参数名,而不是它在所有参数中位置决定的;由于参数可以设置默认值,这允许我们只给出部分参数的取值,而不必是所有的参数。

class Robot1(
        val code:String,
        val battery: String? = null,
        val height: Int? = null,
        val weight: Int? = null
){
    override fun toString(): String {
        return "robot[code = $code,battery=$battery,height = $height,weight = $weight]"
    }
}

fun main() {
    val robot1 = Robot1("005")
    val robot2 = Robot1("006",battery = "MM")
    val robot3 = Robot1("007",height = 50,weight = 30)
    println(robot1)
    println(robot2)
    println(robot3)
}

相比构建者模式,通过具名的可选参数构造类具有很多优点:

  • 代码变得十分简单,这不仅表现在Robot 类的结构体代码量,我们在声明Robot对象时的语法也要更加简洁;
  • 声明对象时,每个参数都可以是显式的,并且无须按照顺序书写,非常方便灵活。
  • 由于Robot类的每一个对象都是val 声明的。相较构建者中用var 的方案更加安全,这在要求多线程并发安全的业务场景中会显得更有优势。
require 方法对参数进行约束

构建者模式的另一个作用:就是可以在build 方法中对参数添加约束条件。
示例:假设一个机器人的重量必须根据电池的型号决定,那么在未传入电池型号之前,便不能对weight属性进行赋值,否则就会抛出异常。

fun build():Robot{
            if(weight!=null && battery == null){
                throw IllegalArgumentException("battery should be determined when setting weight.")
            }else{
                return Robot(code,battery, height, weight)
            }
        }

实现同样的需求,可以在Robot类的init方法中增加效验代码。然而在Kotlin中,我们在类或函数中还可以使用require 关键字进行函数参数限制,本质上它是一个内联的方法,有点类似于Java中的assert。

class Robot1(
      val code:String,
      val battery: String? = null,
      val height: Int? = null,
      val weight: Int? = null
){
  init {
      require(weight==null || battery!=null){
          "battery should be determined when setting weight."
      }
  }
  override fun toString(): String {
      return "robot[code = $code,battery=$battery,height = $height,weight = $weight]"
  }
}

Kotlin中的require 方法可以让我们的参数约束代码在语意上变得更友好。
在Kotlin中我们可以尽量避免使用构建者模式,因为Kotlin支持具名的可选参数,这让我们可以在构造一个具有多个可选参数类的场景中,设计出更加简洁并利于维护的代码。

行为型模式

koltin中的观察者模式

观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象,这样一来,当被观察者的状态发生改变时,需要通知相应的观察者,使这些观察者对象能够自动更新。

观察者模式无非在做来两件事:订阅者(也称为观察者observer)添加或删除对发布者的状态监听。
发布者状态改变时,将事件通知给监听它的所有观察者,然后观察者执行响应逻辑。
示例:通过java.util.Observable类和java.util.Observer 接口实现 一个动态更新股价的例子。

class StockUpdate :Observable(){
    val observers = mutableSetOf<Observer>()//观察者集合
    //通知观察者
    fun setStockChanged(price:Int){
        this.observers.forEach { it.update(this,price) }
    }
}

//观察者
class StockDisplay:Observer{
    override fun update(o: Observable?, arg: Any?) {
        if(o is StockUpdate){
            println("The latest stock price is $arg")
        }
    }
}

fun main() {
    val su = StockUpdate()
    val sd  = StockDisplay()
    su.observers.add(sd)
    su.setStockChanged(100)
}

首先创建一个可被观察的发布者类StockUpdate,他维护了一个监听其变化的观察者对象observers 集合。通过它的add和remove 方法进行管理。当StockUpdate 类对象执行setStockChanged 方法之后,表明股价发生变化,那么就会将更新的股价传递给观察者,执行其update 方法来执行响应逻辑。
Koltin的标准库额外引入了可被观察的委托属性。

Observable
interface StockUpdateListener{
    fun onRise(price: Int)
    fun onFall(price: Int)
}

//被观察者
class StockUpdate :Observable(){
    val listeners = mutableSetOf<StockUpdateListener>()
    // Delegates.observable 提供了三个参数 依次代表委托属性的元数据KProperty对象,旧值和新值。
    var price:Int by Delegates.observable(0){ _, old, new ->
        listeners.forEach {
            if(new > old) it.onRise(price) else it.onFall(price)
        }
    }
}

//观察者
class StockDisplay:StockUpdateListener{
    override fun onRise(price: Int) {
        println("The latest stock price has risen to $price")
    }

    override fun onFall(price: Int) {
        println("The latest stock price has fell to $price")
    }
}

fun main() {
    val su = StockUpdate()
    val sd  = StockDisplay()
    su.listeners.add(sd)
    su.price = -20
    su.price = 40
}
>>>
The latest stock price has fell to -20
The latest stock price has risen to 40
Vetoable

有些时候,我们并不希望监控的值可以被随心所欲地修改。实际上,你可能希望对某些改值的情况就行否决。Vetoable 提供了一种功能,在被赋新值生效之前提前进行截获,然后判断是否接受它。

fun main() {
    var value :Int by Delegates.vetoable(0){ prop,old,new ->
        new > 0
    }
    value = 1
    println(value)
    value = -2
    println(value)
}
>>>
1
1
高阶函数简化策略模式、模板方法模式
遵循开闭原则:策略模式

示例:假设现在有一个表示游泳运动员的抽象类Swimmer,有一个游泳的方法,swim。

class Swimmer {
    fun swim(){
        println("I am swimming ...")
    }
}

fun main() {
    val shaw = Swimmer()
    shaw.swim()
}

用于shaw在游泳方面很有天赋,他很快掌握了蛙泳,仰泳,自由泳多种姿势。所以必须swim方法进行改造。

class Swimmer {
    fun breaststroke(){
        println("I am breaststroke ...")
    }
    fun backstroke(){
        println("I am backstroke ...")
    }
    fun freestyle(){
        println("I am freestyle ...")
    }
}

这样并不是一个好的设计,并不是所有运动员都掌握了这三种游泳方式,如果每个Swimmer 类对象都可以调用所有方法,显得比较危险。其次后续难免会有新的行为方法加入,直接修改Swimmer 类的方式违背了开放封闭原则。
本质上,策略模式做的事情就是将不同的行为策略(Strategy)进行独立封装,与类在逻辑上解耦。然后根据不同的上下文切换不同的策略,然后用类对象进行调用。

interface SwimStrategy{
    fun swim()
}

class Breaststroke:SwimStrategy{
    override fun swim() {
        println("I am breaststroke ...")
    }
}

class Backstroke:SwimStrategy{
    override fun swim() {
        println("I am Backstroke ...")
    }
}

class Freestyle:SwimStrategy{
    override fun swim() {
        println("I am Freestyle ...")
    }
}

class Swimmer(val strategy: SwimStrategy){
    fun swim(){
        strategy.swim()
    }
}

fun main() {
    val weekendShaw = Swimmer(Freestyle())
    weekendShaw.swim()
    val weekdayShaw = Swimmer(Breaststroke())
    weekdayShaw.swim()
}

这个方案实现了解耦和复用的目的。且很好实现了在不同场景切换采用不同的策略。

高阶函数抽象算法

由于策略类的目的非常明确,仅仅是针对行为算法的一种抽象,所以使高阶函数是一种很好的替代思路。

class Swimmer(val swimming:()->Unit){
    fun swim(){
        swimming()
    }
}

fun main() {
    val weekendShaw = Swimmer(::freestyle)
    weekendShaw.swim()
    val weekdayShaw = Swimmer(::breaststroke)
    weekdayShaw.swim()
}

fun breaststroke(){println("I am breaststroke ...")}
fun backstroke(){println("I am backstroke ...")}
fun freestyle(){println("I am freestyle ...")}
模板方法模式:高阶函数代替继承

模板方法模式的定义:定义一个算法中的操作框架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构,即可重定义该算法的某些特定步骤。
示例:去市民事务中心办事时,一般会有以下几个具体的步骤:

  1. 排队取号等待。
  2. 根据自己的需求办理个性化的业务。
  3. 对服务人员的态度进行评价。
    这是一个典型适用模板方法的模式,办事步骤是一个算法大纲:其中步骤1,3都是相同的,而步骤2 则可以根据实际需求个性化选择。实现一个抽象类,他定义了这个例子的操作框架。
abstract class CivicCenterTask {
    fun execute(){
        this.lineUp()
        this.askForHelp()
        this.evaluate()
    }

    private fun evaluate(){
        println("evaluaten service attitude")
    }

    abstract fun askForHelp()

    private fun lineUp() {
        println("line up to take a number")
    }
}

class PullSocialSecurity:CivicCenterTask(){
    override fun askForHelp() {
        println("ask for pulling this social security")
    }
}

class ApplyForCitizenCard:CivicCenterTask(){
    override fun askForHelp() {
        println("apply for a citizen card")
    }
}

fun main() {
    val pss = PullSocialSecurity()
    pss.execute()
    val afcc = ApplyForCitizenCard()
    afcc.execute()
}

>>>
line up to take a number
ask for pulling this social security
evaluaten service attitude
--------------------------------
line up to take a number
apply for a citizen card
evaluaten service attitude

利用高阶函数进行简化。

class CivicCenterTask {
    fun execute(askForHelp:()->Unit){
        this.lineUp()
        askForHelp()
        this.evaluate()
    }

    private fun evaluate(){
        println("evaluaten service attitude")
    }

    private fun lineUp() {
        println("line up to take a number")
    }
}

fun pullSocialSecurity(){
    println("ask for pulling this social security")
}

fun applyForCitizenCard(){
    println("apply for a citizen card")
}

fun main() {
    CivicCenterTask().execute(::pullSocialSecurity)
    CivicCenterTask().execute(::applyForCitizenCard)
}
运算符重载和迭代器模式

迭代器(iterator)是Java中我们非常熟悉的东西了,数据结构如List 和Set 都内置了迭代器。
迭代器模式的核心作用就是将遍历和实现分离开来。

实现1:实现Iterator接口
data class Book(val name:String)
class Bookcase(val books:List<Book>):Iterator<Book>{
    private val iterator:Iterator<Book>
    init {
        this.iterator = books.iterator()
    }
    override fun hasNext() = this.iterator.hasNext()

    override fun next() = this.iterator.next()
}

fun main() {
    val bookcase = Bookcase(
            listOf(Book("Dive into kotlin"),Book("Thinking in Java"))
    )
    //for (book in bookcase){
    //    println("The book name is ${book.name}")
    //}
    while(bookcase.hasNext()){
        println("The book name is ${bookcase.next().name}")
    }
}

这两种都可以完成迭代。

方案2:重载iterator 方法
class Bookcase(val books:List<Book>){
    operator fun iterator():Iterator<Book> = this.books.iterator()
}
fun main() {
    val bookcase = Bookcase(
            listOf(Book("Dive into kotlin"),Book("Thinking in Java"))
    )
//    for (book in bookcase){
//        println("The book name is ${book.name}")
//    }
    val iterator = bookcase.iterator()
    while(iterator.hasNext()){
        println("The book name is ${iterator.next().name}")
    }
}
方案3:通过扩展函数

假设book 是一个引入的类,不能修改它的源码。可以用扩展的语法給bookcase类对象增加迭代的功能。

data class Book(val name:String)
class Bookcase(val books:List<Book>){}


//operator fun Bookcase.iterator():Iterator<Book> = books.iterator()

operator fun Bookcase.iterator():Iterator<Book> = object :Iterator<Book>{
    val iterator = books.iterator()
    override fun hasNext() = iterator.hasNext()
    override fun next() = iterator.next()
}
用偏函数实现责任链模式

场景:希望使得多个对象都有机会处理某种类型的请求。
责任链模式的目的就是避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
示例:计算机学院的学生会管理了一个学生会基金,用于各种活动和组织人员工作的开支,当要发生一笔支出时,如果金额在100之内,可由各个分部长审批,如果金额超过了100元那么就需要会长同意;但假设金额比较大达到了500以上,那么就需要学院辅导员批准。超过1000元默认打回申请。

data class ApplyEvent(val money: Int, val title: String)

interface ApplyHandler {
    val successor: ApplyHandler?
    fun handleEvent(event: ApplyEvent)
}

class GroupLeader(override val successor: ApplyHandler?) : ApplyHandler {
    override fun handleEvent(event: ApplyEvent) {
        when {
            event.money <= 100 -> println("GroupLeader handled application:${event.title}")
            else -> when (successor) {
                is ApplyHandler -> successor.handleEvent(event)
                else -> println("GroupLeader:This application cannot be handdle.")
            }
        }
    }
}

class President(override val successor: ApplyHandler?) : ApplyHandler {
    override fun handleEvent(event: ApplyEvent) {
        when {
            event.money <= 500 -> println("President handled application:${event.title}")
            else -> when (successor) {
                is ApplyHandler -> successor.handleEvent(event)
                else -> println("President:This application cannot be handdle.")
            }
        }
    }
}

class College(override val successor: ApplyHandler?) : ApplyHandler {
    override fun handleEvent(event: ApplyEvent) {
        when {
            event.money <= 1000 -> println("College handled application:${event.title}")
            else -> println("College:This application is refused")
        }
    }
}


fun main() {
    val college = College(null)
    val president = President(college)
    val groupLeader = GroupLeader(president)

    groupLeader.handleEvent(ApplyEvent(10,"买圆珠笔"))
    groupLeader.handleEvent(ApplyEvent(200,"团队建设"))
    groupLeader.handleEvent(ApplyEvent(560,"举办比赛"))
    groupLeader.handleEvent(ApplyEvent(1200,"举办大型比赛"))
}
>>>
GroupLeader handled application:买圆珠笔
President handled application:团队建设
College handled application:举办比赛
College:This application is refused

当输入参数处于某个责任链环节的有效接收范围之内,该环节才能对其做出正常的处理操作。在编程语言中,我们有一个专门的术语来描述这种情况,这就是偏函数

实现偏函数类型 : PartialFunction

偏函数:指的是定义域x中可能存在某些值在值域y中没有对应的值。
定义 PartialFunction 类型:

//in 逆变 :类型A是类型B的子类型,而K<A>是K<B>的 父类型
//out 协变 :类型A是类型B的子类型,则K<A>是K<B>的 子类型
class PartialFunction<in P1,out R>(private val definetAt:(P1) -> Boolean,private val f:(P1)->R):(P1)->R {
    override fun invoke(p1: P1): R {
        if(definetAt(p1)){
            return f(p1)
        }else{
            throw IllegalArgumentException("Value : ($p1) isn't supported by this function")
        }
    }

    fun isDefinedAt(p1: P1) = definetAt(p1)
}

PartialFunction类的具体作用

  • 声明类对象时需要传入两个构造参数,其中definedAt为效验函数,f为处理函数;
  • 当PartialFunction 类对象执行invoke 方法时,definetAt 会对输入参数p1进行有效性验证;
  • 如果效验通过,则执行f 函数,同时将p1作为参数传递给它,反之则抛出异常。

再利用Kotlin的扩展函数给PartialFunction 类增加一个orElse方法

infix fun <P1,R> PartialFunction<P1,R>.orElse(that:PartialFunction<P1,R>):PartialFunction<P1,R>{
    return PartialFunction({this.isDefinedAt(it)||that.isDefinedAt(it)}){
        when{
            this.isDefinedAt(it) -> this(it)
            else -> that(it)
        }
    }
}

在onELse 方法中可以传入另一个PartialFunction类对象that,他也就是责任链模式中的后继者。
当isDefinedAt 方法执行为false 的时候,就调用that对象来继续处理申请。

用orElse 构建责任链
data class ApplyEvent(val money: Int, val title: String)

val groupLeader = run {
    val definetAt: (ApplyEvent) -> Boolean = { it.money <= 100 }
    val handler: (ApplyEvent) -> Unit = { println("GroupLeader handled application:${it.title}") }
    PartialFunction(definetAt, handler)
}

val president = run {
    val definetAt: (ApplyEvent) -> Boolean = { it.money <= 500 }
    val handler: (ApplyEvent) -> Unit = { println("president handled application:${it.title}") }
    PartialFunction(definetAt, handler)
}

val college = run {
    val definetAt: (ApplyEvent) -> Boolean = { it.money <= 1000 }
    val handler: (ApplyEvent) -> Unit = {
        when{
            it.money>1000 -> println("College:This application is refused")
            else ->println("president handled application:${it.title}")
        }
    }
    PartialFunction(definetAt, handler)
}

fun main() {
    val applyChain = groupLeader orElse president orElse college
    applyChain(ApplyEvent(560,"举办比赛"))
}
ADT 实现状态模式

状态模式:允许一个对象在其内部状态改变时改变他的行为,对象看起来似乎修改了它的类。
状态模式具体表现在:

  • 状态决定行为,对象的行为由它内部的状态决定。
  • 对象的状态在运行期被改变时,它的行为也会因此而改变。从表面上看,同一个对象,在不同的运行时刻,行为是不一样的,就像是类被修改了一样。
    策略模式通过在客户端切换不同的策略实现来改变算法;而策略模式中,对象通过修改内部状态来切换不同的行为方法。
    示例:假设一个饮水机有3种工作状态,分别Wie未启动、制冷模式、制热模式。
sealed class WaterMachineState(open val machine:WaterMachine){
    fun turnHeating(){
        if(this !is Heating){
            println("turn heating")
            machine.state = machine.heating
        }else{
            println("The state is already heating mode")
        }
    }
    fun turnCooling(){
        if(this !is Cooling){
            println("turn cooling")
            machine.state = machine.cooling
        }else{
            println("The state is already cooling mode")
        }
    }
    fun turnOff(){
        if(this !is Off){
            println("turn off")
            machine.state = machine.off
        }else{
            println("The state is already off")
        }
    }
}
class Off(override val machine:WaterMachine):WaterMachineState(machine)
class Heating(override val machine:WaterMachine):WaterMachineState(machine)
class Cooling(override val machine:WaterMachine):WaterMachineState(machine)

class WaterMachine{
    var state:WaterMachineState
    val off = Off(this)
    val heating = Heating(this)
    val cooling = Cooling(this)
    init {
        this.state = off
    }
    fun turnHeating(){
        this.state.turnHeating()
    }

    fun turnCooling(){
        this.state.turnCooling()
    }
    fun turnOff(){
        this.state.turnOff()
    }
}

enum class Moment{
    EARLY_MORNING,
    DRINKING_WATER,
    INSTANCE_NOODLES,
    AFTER_WORK
}

fun waterMachineOps(machine: WaterMachine,moment: Moment){
    when(moment){
        Moment.EARLY_MORNING,Moment.DRINKING_WATER -> when(machine.state){
            !is Cooling -> machine.turnCooling()
        }
        Moment.AFTER_WORK -> when(machine.state){
            !is Off -> machine.turnOff()
        }
        Moment.INSTANCE_NOODLES ->when(machine.state){
            !is Heating -> machine.turnHeating()
        }
    }
}

fun main() {
    val machine = WaterMachine()
    waterMachineOps(machine,Moment.DRINKING_WATER)
    waterMachineOps(machine,Moment.INSTANCE_NOODLES)
    waterMachineOps(machine,Moment.DRINKING_WATER)
    waterMachineOps(machine,Moment.AFTER_WORK)
}

结构型模式

在对象被创建之后,对象的组成及对象之间的依赖关系就成了我们关注的焦点。

装饰者模式:用类委托减少样板代码

在Java中,当我们要给一个类扩展行为的时候通常有两种选择:

  • 设计一个继承它的子类。
  • 使用装饰者模式对该类进行装饰,然后对功能就行扩展。

装饰者模式:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。该模式通过创建一个包装对象,来包裹真实的对象。
装饰者模式做的是以下几件事情:

  • 创建一个装饰类,包含一个需要被装饰类的实例。
  • 装饰类重写所有被装饰类的方法。
  • 在装饰类中对需要增强的功能进行扩展。

装饰者模式很大的优势在于符合“组合优于继承”的设计原则,规避了某些场景下继承所带来的的问题,然而,他有时候也会显得比较啰嗦,因为要重写所有的装饰对象方法,所以可能存在大量的样版代码。
在Kotlin,我们可以让装饰者模式的实现变得更加优雅。 利用类委托特性,可以用by关键字,将修饰类的所有方法委托给一个被装饰的类对象,然后只需要覆写需要装饰的方法即可。

interface MacBook {
    fun getCost():Int
    fun getDesc():String
    fun getProdDate():String
}

class MacBookPro:MacBook{
    override fun getCost() = 10000

    override fun getDesc()="macBook pro"

    override fun getProdDate() = "Late 2011"
}

class ProcessorUpgradeMacbookPro(val macBook: MacBook):MacBook by macBook{
    override fun getCost() = macBook.getCost() + 219
    override fun getDesc() = macBook.getDesc() + ", + 1G Memory"
}

fun main() {
    val macBookPro = MacBookPro()
    val processorUpgradeMacbookPro = ProcessorUpgradeMacbookPro(macBookPro)
    println(processorUpgradeMacbookPro.getCost())
    println(processorUpgradeMacbookPro.getDesc())
}
通过扩展代替装饰者
class Printer {
    fun drawLine(){
        println("-------------------")
    }
    fun drawDottedLine(){
        println(" - -- -- -- -- -- -")
    }
    fun drawStars(){
        println("***************")
    }
}

fun Printer.startDraw(decorated:Printer.() -> Unit){
    println("++++ start drawing ++++")
    decorated()
    println("++++ end drawing ++++")
}

fun main() {
    val printer = Printer()
    printer.startDraw(Printer::drawStars)
    Printer().run {
        startDraw {
            drawDottedLine()
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值