
他们说:“设计模式是解决特定编程语言缺点的方法。” 辩护者Lisp和Schema只是没有说过一个有趣的主张。
但是,似乎Kotlin语言的开发人员确实将此声明铭记在心。
辛格尔顿
当然,想到的第一个模式是Loner。 它以object关键字的形式直接内置到语言中:
object JustSingleton { val value : String = "Just a value" }
现在,可以从包中的任何位置访问JustSingleton.value
字段。
不,这不是看起来像是静态初始化。 让我们尝试在内部延迟一些时间来初始化该字段:
object SlowSingleton { val value : String init { var uuid = "" val total = measureTimeMillis { println("Computing") for (i in 1..10_000_000) { uuid = UUID.randomUUID().toString() } } value = uuid println("Done computing in ${total}ms") } }
延迟初始化发生在第一个调用上:
@org.testng.annotations.Test fun testSingleton() { println("Test started") for (i in 1..3) { val total = measureTimeMillis { println(SlowSingleton.value) } println("Took $total ms") } }
输出为:
Test started Computing Done computing in 5376ms "45f7d567-9b3e-4099-98e6-569ebc26ecdf" Took 5377 ms "45f7d567-9b3e-4099-98e6-569ebc26ecdf" Took 0 ms "45f7d567-9b3e-4099-98e6-569ebc26ecdf" Took 0 ms
请注意,如果不使用该对象,尽管该对象仍在您的代码中定义,但该操作将花费0毫秒。
val total = measureTimeMillis {
输出:
Test started Took 0 ms Took 0 ms Took 0 ms
装饰器
然后是装饰器 。 这种模式允许您在其他一些类之上添加一些功能。 是的,IntelliJ可以为您创建它。 但是科特林走得更远。
每次我们在HashMap中添加新密钥时,都会收到有关此消息的信息吗?
在构造函数中,您定义一个实例,使用by关键字将所有方法委托给该实例。
class HappyMap<K, V>(val map : MutableMap<K, V> = mutableMapOf()) : MutableMap<K, V> by map{ override fun put(key: K, value: V): V? { return map.put(key, value).apply { if (this == null) { println("Yay! $key") } } } }
请注意,我们可以通过方括号访问地图的元素,并以与常规HashMap相同的方式使用所有其他方法。
@org.testng.annotations.Test fun testDecorator() { val map = HappyMap<String, String>() val result = captureOutput { map["A"] = "B" map["B"] = "C" map["A"] = "C" map.remove("A") map["A"] = "C" } assertEquals(mapOf("A" to "C", "B" to "C"), map.map) assertEquals(listOf("Yay! A", "Yay! B", "Yay! A"), (result)) }
工厂方法
通过Companion对象 ,可以轻松实现Factory方法 。 这是对象控制其初始化过程以在其内部隐藏一些秘密的模式。
class SecretiveGirl private constructor(val age: Int, val name: String = "A girl has no name", val desires: String = "A girl has no desires") { companion object { fun newGirl(vararg desires : String) : SecretiveGirl { return SecretiveGirl(17, desires = desires.joinToString(", ")) } fun newGirl(name : String) : SecretiveGirl { return SecretiveGirl(17, name = name) } } }
现在没有人可以改变SecretiveGirl的年龄:
@org.testng.annotations.Test fun FactoryMethodTest() { // Cannot do this, constructor is private // val arya = SecretiveGirl(); val arya1 = SecretiveGirl.newGirl("Arry") assertEquals(17, arya1.age) assertEquals("Arry", arya1.name) assertEquals("A girl has no desires", arya1.desires) val arya2 = SecretiveGirl.newGirl("Cersei Lannister", "Joffrey", "Ilyn Payne") assertEquals(17, arya2.age) assertEquals("A girl has no name", arya2.name) assertEquals("Cersei Lannister, Joffrey, Ilyn Payne", arya2.desires) }
策略策略
今天的最后一个是战略 。 由于Kotlin具有高阶函数 ,因此实现此模式也非常简单:
class UncertainAnimal { var makeSound = fun () { println("Meow!") } }
并动态更改行为:
@org.testng.annotations.Test fun testStrategy() { val someAnimal = UncertainAnimal() val output = captureOutput { someAnimal.makeSound() someAnimal.makeSound = fun () { println("Woof!") } someAnimal.makeSound() } assertEquals(listOf("Meow!", "Woof!"), output) }
请注意,这确实是一种策略模式,您不能更改方法签名(您好,JS!)
// Won't compile! someAnimal.makeSound = fun (message : String) { println("$message") }
所有代码都可以在我的GitHub页面上找到 。
如果您有兴趣了解有关Kotlin及其内置设计模式的更多信息,那么有一本很棒的书“ Kotlin in Action” 。 即使您不打算在不久的将来使用此语言,您也会喜欢它(尽管没有理由不这样做)。