Kotlin小知识点 - Delegation Pattern

Posted by Undervoid on January 23, 2019

Kotlin小知识集合

总结一些 Kotlin 方面的调用,会不断的持续更新这个文章

设计模式

单例模式

(https://juejin.im/post/5acf49a06fb9a028d700ff4d) (https://medium.com/@programmerr47/singletons-in-android-63ddf972a7e7) (https://medium.com/@BladeCoder/kotlin-singletons-with-argument-194ef06edd9e)

单例模式实现几种方式

1.饿汉式实现

代理模式

简介:在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧. 注意这里的委托模式其实是一种Composing

interface I {
     void f();
     void g();
 }
 
 class A implements I {
     public void f() { System.out.println("A: doing f()"); }
     public void g() { System.out.println("A: doing g()"); }
 }
 
 class B implements I {
     public void f() { System.out.println("B: doing f()"); }
     public void g() { System.out.println("B: doing g()"); }
 }
 
 class C implements I {
     // delegation
     I i = new A();
 
     public void f() { i.f(); }
     public void g() { i.g(); }
 
     // normal attributes
     public void toA() { i = new A(); }
     public void toB() { i = new B(); }
 }
 
 
 public class Main {
     public static void main(String[] args) {
         C c = new C();
         c.f();     // output: A: doing f()
         c.g();     // output: A: doing g()
         c.toB();
         c.f();     // output: B: doing f()
         c.g();     // output: B: doing g()
     }
 }

kotlin 的委托模式

类代理

interface Base {
    val message: String
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
    // This property is not accessed from b's implementation of `print`
    override val message = "Message of Derived"
}

fun main() {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print()
    println(derived.message)
}

输出结果,可以看出他其实是两种class的结合

BaseImpl: x = 10
Message of Derived

属性代理

先来谈谈kotlin自带的属性代理吧

  • Lazy
     val lazyData: Double by lazy {
    println("Initializing lazyData")
    2.0
}
fun main(args: Array<String>) {
    println(lazyData)
    println(lazyData)
}
结果是 
Initializing lazyData
2.0
2.0

这里其实牵扯到一个线程问题,该值只在一个线程中计算,并且所有线程会看到相同的值。如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。 而如果你确定初始化将总是发生在单个线程,那么你可以使用 LazyThreadSafetyMode.NONE 模式, 它不会有任何线程安全的保证和相关的开销。

  • Observable
 var observableData: String by Delegates.observable("Initial value") {
    property, oldValue, newValue ->
    println("${property.name}: $oldValue -> $newValue")
}

fun main(args: Array<String>) {
    observableData = "New value"
    observableData = "Another value"
}
结果是 
observableData: Initial value -> New value
observableData: New value -> Another value

可以想到一个在android程序中使用的实例,就是通知一个adapter是否变化数据来更新UI`

example:

var items: List by Delegates.observable(emptyList()) {
    _, _, _ -> notifyDataSetChanged()
}
  • Vetoable 和 Observable很相近但是会选择一个condition来判断新改变的value 是否可以改变
var vetoableData: Int by Delegates.vetoable(0) {
    property, oldValue, newValue ->
    println("${property.name}: $oldValue -> $newValue")
    newValue >= 0
}

fun main(args: Array<String>) {
    vetoableData = -1
    println("vetoableData = $vetoableData")
    vetoableData = 1
    println("vetoableData = $vetoableData")
}

结果是

vetoableData: 0 -> -1
vetoableData = 0
vetoableData: 0 -> 1
vetoableData = 1
  • Storing Map
//这里也可以被MutableMap 代替 var 变量
class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

结果是
println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25

自定义 Delegation 类

其实最常见的就是 bindView


class MainActivity : AppCompatActivity() ,MainContract.View {

    val imageView : ImageView by bindView(R.id.imageView)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView.setImageDrawable(null)
    }
}

fun <T: View> bindView( id : Int): FindView<T> = FindView(id)

class FindView<T : View >(val id:Int) : ReadOnlyProperty<Any?,T>{

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {

        if(this.value == null) {
            this.value = (thisRef as Activity).findViewById<T>(id)
        }
        return this.value?:throw RuntimeException("can not find this view")
    }

    var value : T? = null
}