持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
前言
什么是接口隔离原则?不强迫开发者实现不想使用的方法。Java的做法就是建立单一接口,不要建立内容量庞大的接口,尽量细分接口,同时接口中的方法也要尽量少。换句话说,为每个类建立专用的接口,而不要建立一个很多内容的接口供所有依赖它的类去调用。
示例
相信大家都用过TextWatcher
这个接口类,当我们在等待TextView
发生改变的时候需要使用此接口,而且开发过程使用频率极高,在开发中被广泛使用。假设我们需要在用户输入字符的时候显示一个toast
提示,为此我们一般都需要通过addTextChangedListener
来实现TextWatcher
接口,但是我们又必须被迫的实现TextWatcher
的3个回调。
代码如下所示:
et_info.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int){
// 文本改变前
}
override fun onTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int) {
Toast.makeText(this, charSequence.toString, Toast.LENGTH_LONG).show()
}
override fun afterTextChanged(charSequence: Editable?) {
// 文本改变前
}
})
但实际上,我们只需要使用onTextChanged
这一个回调来实现我们的Toast
功能。我们不需要使用beforeTextChanged
和afterTextChanged
函数,那么这样写就违反了接口隔离原则。
那么现在问题就是:我们如何在TextWatcher
接口中只使用onTextChanged
这一个函数呢?
解决方法
我们可以借助 Kotlin 中的扩展函数和内联函数,来满足我们对接口隔离的需求,首先在EditText类上实现一个扩展函数,将需要被执行的函数以lambda的形式传递到TextWatcher
的onTextChanged
这一函数内执行。
代码如下所示:
inline fun EditText.onTextChange(crossinline invoke: (String) -> Unit) {
this.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int){
// 文本改变前
}
override fun onTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int){
// 执行外部逻辑
invoke(charSequence.toString())
}
override fun afterTextChanged(charSequence: Editable?) {
// 文本改变前
}
})
}
在这里,我们将一个 lambda
函数invoke
作为参数添加到我们的 onTextChange
扩展函数。函数invoke
将 String
作为参数。每当调用 TextWatcher
中的回调函数 onTextChanged
时,我们将调用函数invoke
并将字符串参数传递给它。外部 EditText
在使用的时候就会比较便捷。
代码如下所示:
et_info.onTextChange{
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
代码分析
-
inline:内联函数的使用增强了高阶函数的性能。内联函数告诉编译器将参数和函数复制到调用站点。
-
crossinline:crossinline修饰符以避免非本地返回。如果我们在 lambdas 中添加了返回,它将允许非本地返回并将代码留在其下方。为了避免这种情况,我们在 lambda 函数invoke之前使用 crossinline 修饰符。
总结
当我们尝试实现包含大量功能的接口,并且只需要使用其中极个别的回调函数时,我们可以采用这种方式来提高我们代码的简洁性与高效性。