Kotlin 语言下设计模式的不同实现

时间:2022-11-03 18:48:21

偶然在 Github 上看到 dbacinski 写的 Kotlin 语言下设计模式的不同实现(这里的不同是相对于 Java 语言的),有些实现非常好,但是有些实现的例子不是很赞同。所以自己写了 Kotlin 语言版本的 23 种设计模式的实现,充分利用 Kotlin 的语法糖,例如单例模式、策略模式等可以很巧妙地实现,其他实现方式与 Java 不变的也有代码示例,就当是回顾设计模式。

创建型模式

工厂方法模式

工厂方法把创建对象的过程抽象为接口,由工厂的子类决定对象的创建,Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Product {
val name: String
}
 
class ProductA(override val name: String = "ProductA") : Product
class ProductB(override val name: String = "ProductB") : Product
 
interface Factory {
fun makeProduct(): Product
}
 
class FactoryA : Factory {
override fun makeProduct() = ProductA()
}
class FactoryB : Factory {
override fun makeProduct() = ProductB()
}

抽象工厂模式

工厂方法针对一种产品,而抽象工厂是针对一系列产品,为每种产品定义一个工厂方法,工厂子类负责创建该系列的多种产品,Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class SeriesATextView(context: Context?) : TextView(context)
class SeriesBTextView(context: Context?) : TextView(context)
 
class SeriesAButton(context: Context?) : Button(context)
class SeriesBButton(context: Context?) : Button(context)
 
interface AbstractFactory {
val context: Context?
fun makeTextView(): TextView
fun makeButton(): Button
}
 
class SeriesAFactory(override val context: Context?) : AbstractFactory {
override fun makeTextView() = SeriesATextView(context)
override fun makeButton() = SeriesAButton(context)
}
 
class SeriesBFactory(override val context: Context?) : AbstractFactory {
override fun makeTextView() = SeriesBTextView(context)
override fun makeButton() = SeriesBButton(context)
}

建造者模式

建造者模式是为了构建复杂对象的,一般情况下,Kotlin 中使用标准的apply函数就可以了,例如下面创建 Dialog 的例子:

1
2
3
4
5
6
val dialog = Dialog(context).apply {
setTitle("DialogA")
setCancelable(true)
setCanceledOnTouchOutside(true)
setContentView(contentView)
}

不过上面的代码中在 apply 里的 lambda 表达式里可以调用 Dialog.show() 等其他与构建对象无关的方法,或者不想公开构造函数,只想通过 Builder 来构建对象,这时可以使用 Type-Safe Builders:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Car (
val model: String?,
val year: Int
) {
private constructor(builder: Builder) : this(builder.model, builder.year)
 
class Builder {
var model: String? = null
var year: Int = -1
 
fun build() = Car(this)
}
 
companion object {
inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
}
}
 
// usage
val car = Car.build {
model = "John One"
year = 2017
}

原型模式

原型模式是以一个对象为原型,创建出一个新的对象,在 Kotlin 下很容易实现,因为使用 data class 时,会自动获得equalshashCodetoStringcopy方法,而copy方法可以克隆整个对象并且允许修改新对象某些属性。

1
2
3
4
data class EMail(var recipient: String, var subject: String?, var message: String?)
 
val mail = EMail("abc@example.com", "Hello", "Don't know what to write.")
val copy = mail.copy(recipient = "other@example.com")

单例模式

单例模式在 Kotlin 下直接使用object就行了。想要实现懒汉式单例或更详细的内容,请看之前的文章 Kotlin 设计模式之单例模式

结构型模式

适配器模式

适配器模式是把一个不兼容的接口转化为另一个类可以使用的接口,Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Target {
fun request()
}
 
interface Adaptee {
fun ask()
}
 
class Adapter(val wrapper: Adaptee) : Target {
override fun request() {
wrapper.ask()
}
}

桥接模式

桥接模式可以分离某个类存在两个独立变化的纬度,把多层继承结构改为两个独立的继承结构,在两个抽象层中有一个抽象关联,Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Color {
fun coloring();
}
 
class RedColor : Color { ... }
class BlueColor : Color { ... }
 
interface Pen {
val colorImpl: Color // this is bridge
fun write()
}
 
class BigPen(override val colorImpl: Color) : Pen { ... }
class SmallPen(override val colorImpl: Color) : Pen { ... }

组合模式

组合模式是对树形结构的处理,让调用者忽视单个对象和组合结构的差异,通常会新增包含叶子节点和容器节点接口的抽象构件 Component,Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface AbstractFile { // Component
var childCount: Int
fun getChild(i: Int): AbstractFile
fun size(): Long
}
 
class File(val size: Long, override var childCount: Int = 0) : AbstractFile {
override fun getChild(i: Int): AbstractFile {
throw RuntimeException("You shouldn't call the method in File")
}
 
override fun size() = size
}
 
class Folder(override var childCount: Int) : AbstractFile {
override fun getChild(i: Int): AbstractFile {
...
}
 
override fun size(): Long {
return (0..childCount)
.map { getChild(it).size() }
.sum()
}
}

装饰模式

装饰模式可以给一个对象添加额外的行为,在 Kotlin 中可以通过扩展函数简单的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Text(val text: String) {
fun draw() = print(text)
}
 
fun Text.underline(decorated: Text.() -> Unit) {
print("_")
this.decorated()
print("_")
}
 
// usage
Text("Hello").run {
underline {
draw()
}
}

外观模式

外观模式是为一个复杂的子系统提供一个简化的统一接口,Kotlin 下的实现与 Java 一样,下面我直接使用 dbacinski 的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class ComplexSystemStore(val filePath: String) {
init {
println("Reading data from file: $filePath")
}
 
val store = HashMap<String, String>()
 
fun store(key: String, payload: String) {
store.put(key, payload)
}
 
fun read(key: String): String = store[key] ?: ""
 
fun commit() = println("Storing cached data: $store to file: $filePath")
}
 
data class User(val login: String)
 
//Facade:
class UserRepository {
val systemPreferences = ComplexSystemStore("/data/default.prefs")
 
fun save(user: User) {
systemPreferences.store("USER_KEY", user.login)
systemPreferences.commit()
}
 
fun findFirst(): User = User(systemPreferences.read("USER_KEY"))
}
 
// usage
val userRepository = UserRepository()
val user = User("dbacinski")
userRepository.save(user)
val resultUser = userRepository.findFirst()

享元模式

享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了可共享内部状态和不可共享外部状态。Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
enum class Color {
black, white
}
 
open class Chess(val color: Color) {
fun display(pos: Pair<Int, Int>) {
println("The $color chess at position $pos")
}
}
 
class BlackChess : Chess(color = Color.black)
class WhiteChess : Chess(color = Color.white)
 
object ChessFactory {
private val table = Hashtable<Color, Chess>()
 
init {
table.put(Color.black, BlackChess())
table.put(Color.white, WhiteChess())
}
 
fun getChess(color: Color) = table[color]!!
}
 
// usage
val blackChess = ChessFactory.getChess(Color.black)
val whiteChess = ChessFactory.getChess(Color.white)
blackChess.display(Pair(9, 5))
whiteChess.display(Pair(5, 9))
whiteChess.display(Pair(2, 3))

代理模式

代理模式是使用一个代理对象来访问目标对象的行为,Kotlin 下的实现与 Java 一样,下面我也直接使用 dbacinski 的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface File {
fun read(name: String)
}
 
class NormalFile : File {
override fun read(name: String) = println("Reading file: $name")
}
 
// proxy
class SecuredFile : File {
val normalFile = NormalFile()
var password: String = ""
 
override fun read(name: String) {
if (password == "secret") {
println("Password is correct: $password")
normalFile.read(name) // call target object behavior
} else {
println("Incorrect password. Access denied!")
}
}
}

行为型模式

职责链模式

职责链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。Kotlin 下的实现与 Java 一样,看下面这个简易的 Android 事件传递的例子,event 不知道是否被 ViewGroup 拦截并处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface EventHandler {
var next: EventHandler?
fun handle(event: MotionEvent): Boolean
}
 
open class View : EventHandler {
override var next: EventHandler? = null
override fun handle(event: MotionEvent): Boolean {
return onTouchEvent()
}
open fun onTouchEvent() : Boolean {
...
return false
}
}
 
open class ViewGroup : View() {
override fun handle(event: MotionEvent): Boolean {
if (onInterceptTouchEvent(event)) return onTouchEvent()
else return next?.handle(event)!!
}
 
open fun onInterceptTouchEvent(event: MotionEvent): Boolean { // 是否拦截事件
...
return false
}
}

命令模式

命令模式是将请求封装为命令对象,解耦请求发送者与接收者,对请求排队或者记录请求日志,以及支持可撤销的操作。Kotlin 下的实现与 Java 一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
interface Command {
var value: Int
val param: Int
fun execute()
fun undo()
}
 
class AddCommand(override var value: Int, override val param: Int) : Command {
override fun execute() {
value += param
println("execute add command and value:$value")
}
override fun undo() {
value -= param
println("undo add command and value:$value")
}
}
 
class MultiplyCommand(override var value: Int, override val param: Int) : Command {
override fun execute() {
value *= param
println("execute multiply command and value:$value")
}
override fun undo() {
value /= param
println("undo multiply command and value:$value")
}
}
 
class Calculator {
val queue = mutableListOf<Command>()
fun compute(command: Command) {
command.execute()
queue.add(command)
}
fun undo() {
queue.lastOrNull()?.undo()
queue.removeAt(queue.lastIndex)
}
}

解释器模式

解释器模式是定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。因为使用频率较低,而且 Kotlin 中也没有特殊的实现,所以就不举例说明了。

迭代器模式

迭代器模式提供一种遍历聚合对象中的元素的一种方式,在不暴露底层实现的情况下。在 Kotlin 下,定义 operator fun iterator() 迭代器函数,或者是作为扩展函数时,可以在 for 循环中遍历。

1
2
3
4
5
6
7
8
9
10
11
class Sentence(val words: List<String>)
 
operator fun Sentence.iterator(): Iterator<String> = words.iterator()
// or
operator fun Sentence.iterator(): Iterator<String> = object : Iterator<String> {
val iterator = words.iterator()
 
override fun hasNext() = iterator.hasNext()
 
override fun next() = iterator.next()
}

中介者模式

中介者模式用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
interface ChatMediator {
fun sendMsg(msg: String, user: User)
fun addUser(user: User)
}
 
abstract class User(val name: String, val mediator: ChatMediator) {
abstract fun send(msg: String)
abstract fun receive(msg: String)
}
 
class ChatMediatorImpl : ChatMediator {
private val users = mutableListOf<User>()
 
override fun sendMsg(msg: String, user: User) {
users.filter { it != user }
.forEach { it.receive(msg) }
}
 
override fun addUser(user: User) {
users.add(user)
}
}
 
class UserImpl(name: String, mediator: ChatMediator) : User(name, mediator) {
override fun send(msg: String) {
println("$name : sending messages = $msg")
mediator.sendMsg(msg, this)
}
 
override fun receive(msg: String) {
println("$name : received messages = $msg")
}
}

备忘录模式

备忘录模式是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
data class Memento(val fileName: String, val content: StringBuilder)
 
class FileWriter(var fileName: String) {
 
private var content = StringBuilder()
 
fun write(str: String) {
content.append(str)
}
 
fun save() = Memento(fileName, StringBuilder(content))
 
fun restore(m: Memento) {
fileName = m.fileName
content = m.content
}
}

观察者模式

观察者模式是一个对象状态发生变化后,可以立即通知已订阅的另一个对象。在 Kotlin 下可以使用 observable properties,简化实现。

1
2
3
4
5
6
7
8
9
10
11
interface TextChangedListener {
fun onTextChanged(newText: String)
}
 
class TextView {
var listener: TextChangedListener? = null
 
var text: String by Delegates.observable("") { prop, old, new ->
listener?.onTextChanged(new)
}
}

状态模式

状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态可以让对象拥有不同的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
sealed class UserState(val name:String, val isAuthorized: Boolean) {
abstract fun click()
 
class Unauthorized : UserState(name = "Unauthorized", isAuthorized = false) {
override fun click() {
print("User is unauthorized.")
}
}
 
class Authorized(name: String) : UserState(name, isAuthorized = true) {
override fun click() {
print("User is authorized as $name")
}
}
}
 
class User {
private var state: UserState = UserState.Unauthorized()
 
val name: String
get() = state.name
 
val isAuthorized: Boolean
get() = state.isAuthorized
 
fun click() = state.click()
 
fun login(name: String) {
state = UserState.Authorized(name)
}
 
fun logout() {
state = UserState.Unauthorized()
}
}

策略模式

策略模式用于算法的*切换和扩展,分离算法的定义与实现,在 Kotlin 中可以使用高阶函数作为算法的抽象。

1
2
3
4
5
6
7
8
9
10
class Customer(val name: String, val fee: Double, val discount: (Double) -> Double) {
fun pricePerMonth() = discount(fee)
}
 
// usage
val studentDiscount = { fee: Double -> fee/2 }
val noDiscount = { fee: Double -> fee }
 
val student = Customer("Ned", 10.0, studentDiscount)
val regular = Customer("John", 10.0, noDiscount)

模版方法模式

模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成,Kotlin 中使用高阶函数可以避免继承的方式。

1
2
3
4
5
6
7
8
9
class Task {
fun run(step2: () -> Unit, step3: () -> Unit) {
step1()
step2()
step3()
}
 
fun step1() { ... }
}

访问者模式

访问者模式提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface Employee {
fun accept(visitor: Visitor)
}
 
class GeneralEmployee(val wage: Int) : Employee {
override fun accept(visitor: Visitor) = visitor.visit(this)
 
}
 
class ManagerEmployee(val wage: Int, val bonus: Int) : Employee {
override fun accept(visitor: Visitor) = visitor.visit(this)
}
 
interface Visitor {
fun visit(ge: GeneralEmployee)
fun visit(me: ManagerEmployee)
}
 
class FADVisitor : Visitor {
override fun visit(ge: GeneralEmployee) {
println("GeneralEmployee wage:${ge.wage}")
}
override fun visit(me: ManagerEmployee) {
println("ManagerEmployee wage:${me.wage + me.bonus}")
}
}
// other visitor ...

参考资料

Gang of Four Patterns in Kotlin

Design Patterns

设计模式 Java 版

Kotlin 语言下设计模式的不同实现的更多相关文章

  1. Kotlin 语言高级安卓开发入门

    过去一年,使用 Kotlin 来为安卓开发的人越来越多.即使那些现在还没有使用这个语言的开发者,也会对这个语言的精髓产生共鸣,它给现在 Java 开发增加了简单并且强大的范式.Jake Wharton ...

  2. 用Kotlin语言重新编写Plaid APP:经验教训&lpar;II&rpar;

    原文标题:Converting Plaid to Kotlin: Lessons learned (Part 2) 原文链接:http://antonioleiva.com/plaid-kotlin- ...

  3. Kotlin语言学习笔记(1)

    fun main(args: Array<String>) { println("Hello, World!") } 基本语法 声明常量用val,声明变量用var,声明 ...

  4. Kotlin语言学习笔记(5)

    委托模式(Delegation) 类的委托 interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fu ...

  5. Android Kotlin —— 语言结合

    2017 Google I/O 大会开始就宣布,将Kotlin语言作为安卓开发的一级编程语言.        Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains 开发.   Ko ...

  6. 安卓开发(2)—— Kotlin语言概述

    安卓开发(2)-- Kotlin语言概述 Android的官方文档都优先采用Kotlin语言了,学它来进行Android开发已经是一种大势所趋了. 这里只讲解部分的语法. 如何运行Kotlin代码 这 ...

  7. 用Kotlin语言重新编写Plaid APP:经验教训&lpar;I&rpar;

    原文标题:Converting Plaid to Kotlin: Lessons learned (Part 1) 原文链接:http://antonioleiva.com/plaid-kotlin- ...

  8. 释放Android的函数式能量(I):Kotlin语言的Lambda表达式

    原文标题:Unleash functional power on Android (I): Kotlin lambdas 原文链接:http://antonioleiva.com/operator-o ...

  9. c语言下多线程

    原文:c语言下多线程 [问题]创建了10个线程,10个线程公用一个线程体,创建如下: int t1=0,t2=1,t3=2,t4=3,t5=4,t6=5,t7=6,t8=7,t9=8,t10=9; i ...

随机推荐

  1. go语言操作mysql范例(增删查改)

    http://blog.csdn.net/jesseyoung/article/details/40398321 go语言连接mysql简介    go官方仅提供了database package,d ...

  2. Struts学习总结-02 类型转换

    一 类型转换 input.jsp <%@ page language="java" import="java.util.*" pageEncoding=& ...

  3. dreamweaver cs5中提示扩展管理不可用

    下载: Extension Manager CS5.5 for Windows 安装后重启就能用了

  4. Amazon EC2上搭建VPN服务器

    Amazon EC2 提供了一年免费试用,Micro Instance,配置是 1G 内存,共享 CPU,和每月 15G 的流量.搭一个 VPN 服务器绰绰有余了.操作系统我选的是 Amazon Li ...

  5. postgresql使用文档之一 初始化数据存储区

    17.2. 创建一个数据库集群(Database Cluster) 在你能做任何事情之前,你必须在磁盘上初始化一块存储空间.我们称这为一个数据库集群(database cluster). 一个Data ...

  6. css基础之 语法

    CSS 实例 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明: 选择器通常是您需要改变样式的 HTML 元素. 每条声明由一个属性和一个值组成. 属性(property)是您希望设置的样 ...

  7. 初识JAVA,对servlet的理解

    一.WEB开发的简单理解 Web开发是一个指代网页或站点编写过程的广义术语.网页使用 HTML.CSS 和 JavaScript编写.这些页面可能是类似于文档的简单文本和图形.页面也能够是交互式的,或 ...

  8. Lua面向对象设计(转)

    首先对于Lua语言,它没有打算被用来进行大型的程序设计,相反,Lua目标定于小型到中型的程序设计,通常是作为大型系统的一部分,所以它只提供了一套精简的元素,很多高级语言的概念都没有.这样Lua就成为了 ...

  9. UVALive 5099 Nubulsa Expo 全球最小割 非网络流量 n&Hat;3

    主题链接:点击打开链接 意甲冠军: 给定n个点m条无向边 源点S 以下m行给出无向边以及边的容量. 问: 找一个汇点,使得图的最大流最小. 输出最小的流量. 思路: 最大流=最小割. 所以题意就是找全 ...

  10. uva 156 (map)

    暑假培训习题 1.用vector<string>储存string类型的输入单词: 2.将vector中的元素逐一标准化后映射进map中,并给map值加一: 3.新建一个空的vector 4 ...