0.前言
kotlin的委托文章使用教程不胜枚举,不敢说比各位前辈们写的更好。看文章时突然来了一点点灵感,就像聊聊委托代表的思想,该怎么利用委托到实际开发中。
kotlin官网: 委托模式已经证明是实现继承的一个很好的替代方式, 而 Kotlin 可以零样板代码地原生支持它
1.继承的问题
官方说委托模式是实现继承的一个很好的替代方式。那么继承有什么问题,为什么要提代它。
对于Android程序员来说,最常用到继承的地方是BaseActivity,BaseFragment。那么项目中为什么会要设计这两个Base类呢?
对于Activity 和 fragment 可能存在很多通用功能,不可能每个子类把这些通用功能都实现一遍,所以把代码提取到Base类实现,子类继承Base类,就拥有了Base类中包含的所有功能。
现在假定BaseActivity中存在如下功能:
- 数据埋点
- 沉浸式导航栏
- 公共弹窗
- 业务逻辑
- 模板代码
随着业务的发展BaseActivity的代码可能会越来越多,越来越乱,不同功能不同职责的代码都充斥再一个类中非常不好维护。
由于Java和kotlin只支持单继承,假定经过分析后抽象出三个父类,A继承B,B继承C,把业务根据职责差分到不同的类中,形成一条继承线路。
这就造成了继承层级过多的问题。并且继承把扩展功能固化到线路中每个子类节点上了,如果想创建子类并想让他包含某个已经存在的扩展功能,就必须继承父类,顺带拥有了父类继承线路上所有的扩展功能。虽然可能这个子类不需要某个功能,但是它不得不要。
当然也可以重开一条继承支线,只包含需要的功能,这样做的后果是使类关系过于复杂。如果不拆分就会导致Base类代码爆炸。
小结:
继承也不是一无是处,如果类之间的继承结构稳定,关系不复杂就可以使用继承
当代码复杂时过度使用继承可能会带来如下问题:
- 类关系复杂
- 子类可能会功能冗余
- 违背单一职责,代码爆炸,不好维护
2.Java中委托的实现方式
百度百科: 在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
上述问题可以利用委托模式简化,利用官方AppCompatActivity 举例。可以看出AppCompatActivity 自己没有任何实现,只是接受外部方法调用后,在转交给AppCompatDelegate 委托对象的同名方法。
AppCompatDelegate 是真正做事情的。
public class AppCompatActivity {
private AppCompatDelegate mDelegate;
//...省略代码
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(getDelegate().attachBaseContext2(newBase));
}
@Override
public void setTheme(@StyleRes final int resId) {
super.setTheme(resId);
getDelegate().setTheme(resId);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
}
//...省略代码
}
我们也可以借鉴这种写法,把BaseActivity中的复杂逻辑,委托给一个或者多个类实现,简单示例如下:
open class BaseActivity:AppCompatActivity() {
private val dataDelegate =DataDelegate()
private val uiDelegate =UIDelegate()
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
//1.数据埋点
dataDelegate.onCreate()
//2.沉浸式导航栏
uiDelegate.onCreate()
}
}
委托背后的思想:多用组合,少用继承,代码职责单一
实际上继承也可以看做是一种委托,只不过继承是将子类功能全部委托给父类。
组合是将职责不同的代码,分散到不同的类中实现,根据需要自由组合,更符合职责单一的思想
3.Kotlin委托
kotlin官网: 委托模式已经证明是实现继承的一个很好的替代方式, 而 Kotlin 可以零样板代码地原生支持它
经过上面的讨论对于为什么要使用委托应该有一定了解。kotlin通过by关键字从语法层面天生支持委托。
先实现一个小案例,结合委托和Lifecycle实现日志输出的功能 代码如下:
- 定义ILogPrinter 接口,继承LifecycleEventObserver 拥有监听生命周期的能力
- 创建实现类LogPrinter
- 实现print()方法
- 实现onStateChanged() jie'sh
- 打印日志
- LogActivity
- 类头声明接口并通过by关键字把实现委托给LogPrinter
ILogPrinter by LogPrinter() - 在onCreate()调用
lifecycle.addObserver(this)绑定lifecycle组件即可 - 在当前类中任意位置可随意调用print()函数
- 类头声明接口并通过by关键字把实现委托给LogPrinter
interface ILogPrinter :LifecycleEventObserver{
fun print(tag:String,message:String)
}
class LogPrinter : ILogPrinter {
override fun print(tag: String, message: String) {
Log.d(tag, message)
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
val tag = source.javaClass.name
var message = ""
message = when (event) {
Lifecycle.Event.ON_CREATE -> {
"ON_CREATE"
}
Lifecycle.Event.ON_RESUME -> {
"ON_RESUME"
}
Lifecycle.Event.ON_DESTROY -> {
"ON_DESTROY"
}
else -> {
""
}
}
print(tag, message)
}
}
class LogActivity : AppCompatActivity(), ILogPrinter by LogPrinter() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(this)
print("LogActivity","onCreate函数中调用啦")
}
private fun test(){
print("LogActivity","test函数中调用啦")
}
}
小总结
虽然案例很简单,但是也能够体现出委托的特性:把部分实现交由委托类。在实际开发中功能复用 或者 单个类代码过于复杂时可以尝试使用委托优化
语法则非常简单:ILogPrinter by LogPrinter() 类型 by 实现类
属性委托
val/var <属性名>: <类型> by <表达式> by 后面的表达式 是一个对象,属性委托是把当前属性的getter,setter交给委托对象实现。如下代码 通过代理模拟属性默认的赋值取值过程,没有任何其他操作。
private var text :String by StringDelete()
class StringDelete {
private var value =""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
Log.d("StringDelete","getValue 属性名:${property.name} ")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, s: String) {
Log.d("StringDelete","setValue 属性名:${property.name} 属性值:${s}")
value = s
}
}
模拟一个需求,为项目中某写字符串变量添加缓存操作,取值时从缓存中去,赋值时保存到缓存。改动如下:
private var text :String by StringDelete()
class StringDelete {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return Cache.get(property.name)
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, s: String) {
Cache.set(property.name,s)
}
}
可见属性委托也是同样的思想,当有大量属性存在重复逻辑时就可使用属性委托进行代码优化。
Kotlin提供两个接口实现属性属性委托:ReadOnlyProperty ReadWriteProperty 分别对应只读属性和读写属性。方法是一样的只不过预定义了接口 使用更方便
class StringDelete1 :ReadWriteProperty<Any,String>{
override fun getValue(thisRef: Any, property: KProperty<*>): String {
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
}
}
class StringDelete2 :ReadOnlyProperty<Any,String>{
override fun getValue(thisRef: Any, property: KProperty<*>): String {
}
}
4.参考文章
更多的使用细节:
一文彻底搞懂Kotlin中的委托 - 掘金 (juejin.cn)
Kotlin | 委托机制 & 原理 & 应用 - 掘金 (juejin.cn)
应用参考: