《kotlin核心编程》阅读笔记 第九章 设计模式
2021/5/16 1:25:29
本文主要是介绍《kotlin核心编程》阅读笔记 第九章 设计模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
《kotlin核心编程》阅读笔记 第九章 设计模式
- 第九章 设计模式
- 创建型模式
- 伴生对象增强工厂模式
- 使用单例代替工厂类
- 伴生对象创建静态工厂方法
- 扩展伴生对象方法
- 内联函数简化抽象工厂
- 用具名可选参数而不是构建者模式
- 具名的可选参数
- require 方法对参数进行约束
第九章 设计模式
基于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]
实现思路:
- Robot 类内部定义了一个嵌套类Builder,由他负责创建Robot 对象。
- Robot 类的构造函数用private进行修饰,这样可以确保使用者无法直接通过Robot 声明实例。
- 通过Builder类定义set方法来对可选的属性进行设置。
- 最终调用Builder类中的build 方法来返回一个Robot对象。
构建者模式存在的一些不足:
- 如果业务需求的参数很多,代码依然会显得比较冗长。
- 你可能会在使用Builder的时候忘记在最后调用build 方法;
- 由于在创建对象的时候,必须先创建他的构造器,因此额外增加了多余的开销,在某些十分注重性能的情况下,可能就会存在一定的问题。
本质上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支持具名的可选参数,这让我们可以在构造一个具有多个可选参数类的场景中,设计出更加简洁并利于维护的代码。
这篇关于《kotlin核心编程》阅读笔记 第九章 设计模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-06Kotlin委托属性(1)
- 2023-06-15Kotlin协程-那些理不清乱不明的关系
- 2023-06-08[Kotlin Tutorials 21] 协程的取消
- 2023-05-26Kotlin难点
- 2023-02-23【备战春招】第16天 Kotlin实用技巧
- 2023-02-23【备战春招】第15天 Kotlin扩展Extensions技术探秘
- 2023-02-22【备战春招】第14天 深入理解Kotlin注解
- 2023-02-21【备战春招】第12天 深入理解Kotlin类与接口
- 2023-02-21【备战春招】第13天 深入理解Kotlin泛型
- 2023-02-18【备战春招】第10天 Kotlin方法与Lambda表达式