您现在的位置是:首页 >学无止境 >Kotlin 类委托(by)实战网站首页学无止境
Kotlin 类委托(by)实战
前言:
这篇博客,会以一个实际业务场景为例,逐步教你类委托是如何使用的,同时,也会教你一些关于,提高代码质量的实现思想和实现方式。
如果你只想了解类委托,则可以跳过该部分。
有kotlin使用经验的人,想必都有用过by关键字,但绝大部分场景,应该都是使用在初始化类成员上,比如:
// 延迟加载,等到需要使用testBy时,再初始化。
prival val testBy : String? by lazy { intent.getString("xxx") }
// 在initData()方法里用时,testBy才会被赋值。
private fun initData() { println("testBy=$testBy") }
除了这种常见的场景外,还有一种,相对较少用,但却是非常实用的方式,即类委托模式。让我们用一个例子来解释,这样会比较易学易上手。
以我们公司的业务为例,我们公司开发了一款名为G510的AR眼镜,这双眼镜具备以下功能:
- 开/关,闪关灯功能。
- 亮度调节。
- 音量调节。
- 插拔连接(监听)。
- 按键(监听)。
- …其它功能(我们以上面5个为例)
为了操作这双眼镜(对于Android设备,这是个外设),我们需要定义一个眼镜类:
class G510GlassManager
然后,眼镜具备多个功能,新手上来,可能会直接就在该为里面写实现了,这是不对的。
在实现一个功能前,我们得优先考虑抽象,再做具体,先定义基类/接口,再填充定义,
这样写出来的代码,会有更高的可读性,扩展/维护性,移植性。
实现方式如下:
首先,我们定义一个IGlassManager的接口,将一双AR眼镜的最基础的功能,定义在其中。
上面例的5个功能,除了闪光灯,其它的都是最基础的,那么,接口定义如下:
interface IGlassManager{
fun setVolumn(volumn: Float)
fun setLight(light: Float)
fun setConnectListener(listener: ConnectListener)
fun setKeyListener(listener: KeyListener)
}
而G510眼镜,额外具备了开/关闪光灯的功能,因此,我们需要再额外定义一个接口IG510GlassManager:
interface IG510GlassManager: IGlassManager{
fun switchFlashLight(isOpen: Boolean)
}
到这里,我们已经清楚的知道G510眼镜具备的功能(从代码角度看),我们只需要实现IG510GlassManager接口即可,代码如下:
// 伪代码
class G510GlassManager : IG510GlassManager {
override fun setVolumn(volumn: Float) { //todo }
override fun setLight(light: Float) { //todo }
override fun setConnectListener(listener: ConnectListener) { //todo }
override fun setKeyListener(listener: KeyListener) { //todo }
override fun switchFlashLight(isOpen: Boolean) { //todo }
}
此时,我们只需要将对应的功能实现,填上即可。
当G510眼镜有新功能扩展时,我们要做的,不是在G510GlassManager里面加代码,而是在IG510GlassManager里面加方法,这样做的好处如下:
- 可读性高,体现在,G510眼镜,有什么功能,我们都可以从IG510GlassManager接口中,一目了然,而不是面对一堆实现代码。
- 可扩展性高,体现在,后续G510眼镜,有什么新增功能,我们只需要在接口中添加,再在G510GlassManager类里补充实现即可。
- 可维护性高,体现在,你的同事或是你自己(过了一段时间后),需要去修改对应功能的代码时,你或他们不需要从G510GlassManager的海量代码里面找,而是从接口定义中找到对应方法,再跳转过去即可。添加或删除功能时,也只需要增加或删除对应API就好。
说了这么多,是该轮到by上场了,在这之前,我们先完善一下G510GlassManager类:
// 伪代码
class G510GlassManager : IG510GlassManager {
private val glassInstance by lazy { GlassInstance() }
override fun setVolumn(volumn: Float) { glassInstance.setVolumn(volumn) }
override fun setLight(light: Float) { glassInstance.setLight(light) }
override fun setConnectListener(listener: ConnectListener) { glassInstance.setConnectListener(listener) }
override fun setKeyListener(listener: KeyListener) { glassInstance.setKeyListener(listener) }
override fun switchFlashLight(isOpen: Boolean) { glassInstance.switchFlashLight(isOpen)}
}
看起来,好像是没啥毛病,对吧?因为现在这个类代码少。那我们再给它加些需求,增加些代码:
- 监听手机电量变化, 如果电量过底,弹个toast提示。
- 监听手机网络变化,如果断网,弹个toast提示。【后续眼镜可能需要联网,提前加】
加完,代码如下:
// 伪代码
class G510GlassManager : IG510GlassManager {
private val glassInstance by lazy { GlassInstance() }
init{
// 监听手机电量变化 伪代码
setBatteryListener()
// 监听手机网络变化 伪代码
setNetworkListener()
}
private fun setBatteryListener() {
...
...
...
...
...
// 此处省略100行代码
}
private fun setNetworkListener() {
...
...
...
...
...
// 此处省略100行代码
}
override fun setVolumn(volumn: Float) { glassInstance.setVolumn(volumn) }
override fun setLight(light: Float) { glassInstance.setLight(light) }
override fun setConnectListener(listener: ConnectListener) { glassInstance.setConnectListener(listener) }
override fun setKeyListener(listener: KeyListener) { glassInstance.setKeyListener(listener) }
override fun switchFlashLight(isOpen: Boolean) { glassInstance.switchFlashLight(isOpen)}
}
现在,整个G510GlassManager类是不是代码很多,而且,即有操作眼镜的功能,又有非操作眼镜本身的功能。这样,后期维护起来就很麻烦。有没有什么好的办法,可以将这两块代码完美的解耦开来?有,用by。
让我们用by来解决这个问题,在解决之前,我们先定义一个类G510GlassProxy :
class G510GlassProxy : : IG510GlassManager {
private val glassInstance by lazy { GlassInstance() }
override fun setVolumn(volumn: Float) { glassInstance.setVolumn(volumn) }
override fun setLight(light: Float) { glassInstance.setLight(light) }
override fun setConnectListener(listener: ConnectListener) { glassInstance.setConnectListener(listener) }
override fun setKeyListener(listener: KeyListener) { glassInstance.setKeyListener(listener) }
override fun switchFlashLight(isOpen: Boolean) { glassInstance.switchFlashLight(isOpen)}
}
乍眼一看,咋这个类这么熟悉,这不是上面,一开始的G510GlassManager吗?
答对了!先搁着这个疑问,继续看G510GlassManager的下一步优化:
// 伪代码
class G510GlassManager : IG510GlassManager by G510GlassProxy() {
init{
// 监听手机电量变化 伪代码
setBatteryListener()
// 监听手机网络变化 伪代码
setNetworkListener()
}
private fun setBatteryListener() {
...
...
...
...
...
// 此处省略100行代码
}
private fun setNetworkListener() {
...
...
...
...
...
// 此处省略100行代码
}
}
现在G510GlassManager里面就只有网络和电量监听的代码,原先的G510眼镜的操作实现,都经by关键字,委托到G510GlassProxy类里面实现。这样就将G510操作的代码,跟其它代码解耦开来。