Kotlin 类委托(by)实战

前言:
这篇博客,会以一个实际业务场景为例,逐步教你类委托是如何使用的,同时,也会教你一些关于,提高代码质量的实现思想和实现方式。
如果你只想了解类委托,则可以跳过该部分。

有kotlin使用经验的人,想必都有用过by关键字,但绝大部分场景,应该都是使用在初始化类成员上,比如:

// 延迟加载,等到需要使用testBy时,再初始化。
prival val testBy : String? by lazy { intent.getString("xxx") }

// 在initData()方法里用时,testBy才会被赋值。
private fun initData() { println("testBy=$testBy") }

除了这种常见的场景外,还有一种,相对较少用,但却是非常实用的方式,即类委托模式。让我们用一个例子来解释,这样会比较易学易上手。

以我们公司的业务为例,我们公司开发了一款名为G510的AR眼镜,这双眼镜具备以下功能:

  1. 开/关,闪关灯功能。
  2. 亮度调节。
  3. 音量调节。
  4. 插拔连接(监听)。
  5. 按键(监听)。
  6. …其它功能(我们以上面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里面加方法,这样做的好处如下:

  1. 可读性高,体现在,G510眼镜,有什么功能,我们都可以从IG510GlassManager接口中,一目了然,而不是面对一堆实现代码。
  2. 可扩展性高,体现在,后续G510眼镜,有什么新增功能,我们只需要在接口中添加,再在G510GlassManager类里补充实现即可。
  3. 可维护性高,体现在,你的同事或是你自己(过了一段时间后),需要去修改对应功能的代码时,你或他们不需要从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)}
}

看起来,好像是没啥毛病,对吧?因为现在这个类代码少。那我们再给它加些需求,增加些代码:

  1. 监听手机电量变化, 如果电量过底,弹个toast提示。
  2. 监听手机网络变化,如果断网,弹个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操作的代码,跟其它代码解耦开来。

你可能感兴趣的:(Kotlin,kotlin,android,开发语言,by)