Kotlin 语言下设计模式的不同实现
偶然在 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 时,会自动获得equals
、hashCode
、toString
和copy
方法,而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
Kotlin 语言下设计模式的不同实现的更多相关文章
- Kotlin 语言高级安卓开发入门
过去一年,使用 Kotlin 来为安卓开发的人越来越多.即使那些现在还没有使用这个语言的开发者,也会对这个语言的精髓产生共鸣,它给现在 Java 开发增加了简单并且强大的范式.Jake Wharton ...
- 用Kotlin语言重新编写Plaid APP:经验教训(II)
原文标题:Converting Plaid to Kotlin: Lessons learned (Part 2) 原文链接:http://antonioleiva.com/plaid-kotlin- ...
- Kotlin语言学习笔记(1)
fun main(args: Array<String>) { println("Hello, World!") } 基本语法 声明常量用val,声明变量用var,声明 ...
- Kotlin语言学习笔记(5)
委托模式(Delegation) 类的委托 interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fu ...
- Android Kotlin —— 语言结合
2017 Google I/O 大会开始就宣布,将Kotlin语言作为安卓开发的一级编程语言. Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains 开发. Ko ...
- 安卓开发(2)—— Kotlin语言概述
安卓开发(2)-- Kotlin语言概述 Android的官方文档都优先采用Kotlin语言了,学它来进行Android开发已经是一种大势所趋了. 这里只讲解部分的语法. 如何运行Kotlin代码 这 ...
- 用Kotlin语言重新编写Plaid APP:经验教训(I)
原文标题:Converting Plaid to Kotlin: Lessons learned (Part 1) 原文链接:http://antonioleiva.com/plaid-kotlin- ...
- 释放Android的函数式能量(I):Kotlin语言的Lambda表达式
原文标题:Unleash functional power on Android (I): Kotlin lambdas 原文链接:http://antonioleiva.com/operator-o ...
- 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 ...
随机推荐
- 深入理解学习Git工作流(git-workflow-tutorial)
转载:https://segmentfault.com/a/1190000002918123#articleHeader11 人在学习git工作流的过程中,从原有的 SVN 模式很难完全理解git的协 ...
- 关于PSP(个人软件过程)
在第一堂课时,杨老师就提到了PSP(个人软件过程),但是我从2016年3月10日才开始进行粗略的PSP时间管理统计,这是长迭代,用老师的话“差评”.然后在2016年3月11日下午的软件项目管理上,老师 ...
- java自定义注解学习(一)_demo小练习
自定义注解 现在大家开发过程中,经常会用到注解. 比如@Controller 等等,但是有时候也会碰到自定义注解,在开发中公司的记录日志就用到了自定义注解.身为渣渣猿还是有必要学习下自定义注解的. 这 ...
- wordApp.Documents.Open 未将对象引用实例
wordApp.Documents.Open (.........),当我打开的是.docx,能正常打开当是.doc时,却返回空置,显示失败,未能找到文件.......,但其实文件都在 解决方案 WO ...
- 神奇的Redis延迟
最近在做某业务Redis的缩容工作,涉及到数据迁移,而Redis的数据迁移看起来蛮简单的,一对一的数据迁移只需要在slave行配置masterauth 和slaveof 两个参数即可,当然迁移过程中涉 ...
- HTTTP及TCP的超时以及KEEP-ALIVE机制小结
一.HTTP的超时和Keep Alive HTTP Keepalive 机制是http 1.1中增加的一个功能. 在HTTP 1.0中,客户端每发起一个http 请求,等收到接收方的应答之后就断开TC ...
- 牛客网暑期ACM多校训练营(第一场)J Different Integers
链接:https://www.nowcoder.com/acm/contest/139/J 题意: 给你[l,r]问[1,l],[r,n]中有多少个不同的数. 思路: 可以参考上一篇博客:https: ...
- COCI 2018/2019 CONTEST #2 Solution
Problem1 Preokret 第一题一定不是什么难题. 第一个问题在读入的时候判断当前时间是不是在1440及以前就行 第二个问题考虑离线处理,由于每个时刻只能最多发生1个事件那么就弄个桶记录每一 ...
- 【转】汽车CAN总线
概述 CAN(Controller Area Network)总线协议是由 BOSCH 发明的一种基于消息广播模式的串行通信总线,它起初用于实现汽车内ECU之间可靠的通信,后因其简单实用可靠等特点,而 ...
- 【UVA514】铁轨
题目大意:给定 N 个数,编号从 1 到 N,现需要判断,利用一个无限大的栈结构,能否实现到给定序列的转换. 题解:本题一共涉及三个部分的交互,分别是目标序列,栈和初始序列,由栈的顶端进入,顶端弹出性 ...