Kotlin特色风格实现gof设计模式

来源:互联网 发布:大久保利通知乎 编辑:程序博客网 时间:2024/05/10 20:41

虽然设计模式偏重于思想层面,但是不同的编程语言有着其独特的语法展现,这使得在某个特定语言内,可能会更灵活和更有张力的实现某些设计模式。同时,对于kotlin来说,由于其完全兼容Java,若是只是谈设计模式的实现的话,完全可以把java实现的设计模式convert成kotlin就可以了,但是这样的话,便会埋没一些kotlin的特色。

Kotlin对比java而言,其大大扩大了函数的灵活性:高阶函数(可以接受函数作为参数),扩展函数(在一个类的外面,为其声明新的方法,静态编译,实例调用)以及独立函数(不依赖于类/对象,可以独立存在于文件中)等等,这使得其实现设计模式有其独特的风格和张力,因此,本文的目的,不仅仅是简单的用kotlin实现设计模式,而是专注于发现kotlin语言的特色风格和张力,同时研究kotlin这个现代语言的简洁性以及实现某些经典模式的便捷性。

本文只专注怎么用kotlin来进行比较特色的gof设计模式实现,基本不会探讨这些模式的思想和优缺点,需要的自行百度其它资料。本文还提供了安卓示例工程。

接下来,我们先看看一些模式的实现。

策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

  • 定义算法抽象,这里,不再是接口的方式,而是直接使用函数类型作为抽象;
typealias PlayVideo = () -> String
  • 算法实例对象,不再是类实例的方式,而是用子类型函数的方式,提供两个;
val tv: PlayVideo = {    "用电视看视频"//作为返回值}val phone: PlayVideo = {    "---用手机看视频---"}
  • 算法的使用场景以及调用,tv和phone对象可以相互替换,产生不同行为
class Device(var name: String) {    fun play(p: PlayVideo): String {        return p()    }}result.text = device.play(tv)//电视播放视频result.text = device.play(phone)//手机播放视频

整体实现思路同java类似,只是这里将函数列为了一等公民,免去了对象的创建和调用,节省代码,更易理解。

命令模式

命令模式:将请求封装成对象,以便使用不同的请求、日志、队列等来参数化其他对象。命令模式也支持撤销操作。

  • 抽象命令,声明执行的方法;
typealias command = (worker: Worker) -> Unit
  • 命令接口实现”对象”,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作,在这里,是以函数对象的方式出现。
var strCommand: command = { it.addStr('a') }//后缀添加字符avar numCommand: command = { it.addNum(9) }//后缀添加字符9
  • 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
class Worker {    var str: String = ""    fun addStr(s: Char) {        str += s    }    fun addNum(a: Int) {        str += a    }    fun back() {        str = str.substring(0, str.length - 1)    }}
  • 调用者,要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象,以供进行撤销、日志等操作。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
class Client(var aWorker: Worker) {    var comList = ArrayList<command>()    fun execute(com: command) {        com.invoke(aWorker)        comList.add(com)    }    fun undo() {        if (comList.size == 0) {            return        }        aWorker.back()        comList.remove(comList[comList.size - 1])    }    fun show(): String {        return aWorker.str    }}
  • 使用:创建具体的命令对象,并且设置命令对象的接收者
var client = Client(Worker())//创建调用者client.execute(strCommand)//执行添加字符a的命令client.execute(numCommand)//执行添加数字9的命令client.undo()//撤销上一步的命令

观察者模式

观察者模式:有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

  • 定义观察者,是以函数对象的方式进行;
typealias listener=(a: Int) -> Unit
  • 被观察者,可以增加/删除观察者对象。
class Obsevable() {    var lists: ArrayList<listener> = ArrayList()    fun reg(p: listener) {        lists.add(p)    }    fun unReg(p:listener) {        lists.remove(p)    }    fun no(str: Int) {        for (x in lists) {            x.invoke(str)        }    }}
  • 调用执行。
var observer = Obsevable()//声明被观察者对象observer.reg { result.text = "${result.text}-观察者aaa1得到事件$it"}//注册添加一个观察者,不能被取消注册var v3: listener = { toast("v3得到事件$it") }//声明一个可被删除的观察者observer.reg(v3)//注册添加一个观察者v3observer.notify(110)//发生事件,通知观察者observer.unReg(v3)//删除一个观察者v3

通过函数对象的整合,代码实现更加简约。

装饰者模式

在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。不过Kotlin有强大的扩展函数功能,装饰者的实现将会比较简约。

  • 定义行为抽象和最基础的行为;
interface Text {    fun draw(): String}class DefaultText(var text: String) : Text {    override fun draw(): String {        return text    }}
  • 使用扩展函数,声明几个装饰行为。
fun Text.underline(decorated: Text.() -> String): String {    return "_" + this.decorated() + "_"}//给已有行为添加下划线_fun Text.bracket(decorated: Text.() -> String): String {    return "{" + this.decorated() + "}"}//给已有行为添加花括号{}
  • 调用执行。
var text = DefaultText("装饰者")result.text = text.draw()//基础行为result.text = text.underline { text.draw() }//加下划线result.text = text.bracket { text.underline { text.draw() } }//加下划线,再加括号

通过扩展函数,动态添加某些对象的行为是不是相当方便?

整体就先列出这么几个设计模式的实现吧,眼尖的童鞋可以发现,这些设计模式基本都是行为模式,这与kotlin强大且灵活的函数功能是分不开的。而对于其它类型的某些设计模式,kotlin比较难给出比较特色的实现,以后再讨论吧。

作者刘咸尚