Kotlin特色的設計模式

一、單例模式

1.餓漢式

類加載時就初始化,會提前占用內存。
它基于 classloader 機制避免了多線程的同步問題,不過,instance 在類裝載時就實例化

  • Java

public class Singleton {

    private static Singleton S = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return S;
    }
}

  • Kotlin
object Singleton {
}

Kotlin是如何用一行代碼實現單例的呢?我們來看看反編譯后的java代碼:
tools--Kotlin--show Kotlin Bytecode

public final class Singleton {
   public static final Singleton INSTANCE;

   private Singleton() {
   }

   static {
      Singleton var0 = new Singleton();
      INSTANCE = var0;
   }
}

可以看到,object修飾的類實際上就是Java里的靜態單例,而靜態代碼是隨著類加載的,只會加載一次,這樣就實現了餓漢式單例

2. 懶漢式

  • Java
public class Singleton{
   private static Singleton S;
   private Singleton(){
   
   }
   public static getInstance(){
     if(S==null){
        S = new Singleton();
     }
     return S;
   }
   
}

  • Kotlin
class Singleton private constructor() {
    companion object {
        val S: Singleton by lazy { Singleton() }
    }
}

注:by lazy { ... }的初始化默認是線程安全的,并且能保證by lazy { ... }代碼塊中的代碼最多被調用一次,我們深入源碼看一下,lazy修飾符是如何實現懶加載和線程安全的,關鍵方法是SynchronizedLazyImpl(initializer):

image.png

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // final field is required to enable safe publication of constructed instance
    private val lock = lock ?: this

    override val value: T
        get() { //此處判斷_value已被初始化,則直接返回
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {//增加線程同步鎖,防止并發訪問
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {//未初始化時,真正實現初始化的代碼
                    val typedValue = initializer!!()
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
        }

    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE

    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(): Any = InitializedLazyImpl(value)
}

二、建造者模式

主要解決在軟件系統中,有時候面臨著"一個復雜對象"的創建工作,其通常由各個部分的子對象用一定的算法構成;由于需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩定。下面以我們常用的Dialog舉例:

  • Java
public class Dialog {
    private String left;
    private String right;
    private String title;


    static class Builder {
        private String left;
        private String right;
        private String title;

        Builder left(String left) {
            this.left = left;
            return this;
        }

        Builder right(String right){
            this.right = right;
            return this;
        }

        Builder title(String title){
            this.title = title;
            return this;
        }

        Dialog build(){
            Dialog dialog = new Dialog();
            dialog.left = this.left;
            dialog.right = this.right;
            dialog.title = this.title;
            return dialog;
        }


    }
}

class DialogTest{

    void test(){
        Dialog dialog = new Dialog.Builder().left("left").right("right").title("titlle").build();
    }
}

  • Kotlin
class Dialog{
    var left:String =""
    var right:String=""
    var title:String ="title"
}



class DialogTest{
    @Test
    fun test(){
        val dialog = Dialog().apply {
            left = "left"
            right = "right"
            title = "title"
        }
    }
}

關鍵方法在于apply函數,它使得代碼塊能夠在當前對象語境下,訪問其非私有屬性和函數,執行完畢后返回當前對象:

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

三、觀察者模式

  • Java

觀察者

public interface Observer {
     void update(Object object);
}

被觀察者

public abstract class Observable {
    List<Observer> observers = new ArrayList<>();

    public void register(Observer observer){
        this.observers.add(observer);
    }

    public void unRegister(Observer observer){
        this.observers.remove(observer);
    }

    public void notifyAllObservers(Object object){
        for (Observer observer : observers) {
            observer.update(object);
        }
    }
}

  • Kotlin
interface TextChangedListener {
    fun onTextChanged(newText: String)
}

class TextView {
    var listener: TextChangedListener? = null
    var text: String by Delegates.observable("") { prop, old, new ->
        listener?.onTextChanged(new)
    }
}

為了弄清楚如何實現的,我們查看下Delegates.observable的源碼:

/**
     * Returns a property delegate for a read/write property that calls a specified callback function when changed.
     * @param initialValue the initial value of the property.
     * @param onChange the callback which is called after the change of the property is made. The value of the property
     *  has already been changed when this callback is invoked.
     *
     *  @sample samples.properties.Delegates.observableDelegate
     */
    public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }

是不是還沒有看懂,我們只需要關注ObervableProperty這個類就行了,這里值的改變實際上代理給了ObervableProperty


/**
 * Implements the core logic of a property delegate for a read/write property that calls callback functions when changed.
 * @param initialValue the initial value of the property.
 */
public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    /**
     *  The callback which is called before a change to the property value is attempted.
     *  The value of the property hasn't been changed yet, when this callback is invoked.
     *  If the callback returns `true` the value of the property is being set to the new value,
     *  and if the callback returns `false` the new value is discarded and the property remains its old value.
     */
    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

    /**
     * The callback which is called after the change of the property is made. The value of the property
     * has already been changed when this callback is invoked.
     */
    protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}

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

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        val oldValue = this.value
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}

ReadWriteProperty是一個什么東西呢?它在代理模式中用得非常多:

public interface ReadWriteProperty<in R, T> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public operator fun getValue(thisRef: R, property: KProperty<*>): T

    /**
     * Sets the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @param value the value to set.
     */
    public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

當一個值被ReadWritPropery代理時,調用或者賦值都會相應地回調getValue和setValue方法

我們繼續回ObservableProperty,到從命名就可以看出來,其是一個被觀察者,當它的值發生改變時,會回調afterChange方法,最終會傳遞給我們自定義的onChange方法中,實現值的變化監聽,這樣就實現了最簡單的觀察者模式

四、代理模式

在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。下面舉一個經紀人代理明星的例子:

interface People {
    fun speak()
}
class Broker :People{
    override fun speak() {
        print("hello")
    }
}
class Star(private val broker: Broker) :People by broker

class Test{
    @Test
    fun test(){
        val broker = Broker()
        val star = Star(broker)
        star.speak()
    }
}

在Koltin中實現代理模式非常簡單,只需要試用by關鍵字就行了,上面的Star就是通過 傳入的broker實現的代理,我們看到它自身是沒有實現People類的抽象方法的,最終在test()中調用的實際上是其代理對象broker的speak,在Kotlin中by關鍵字主要有如下幾點作用:

  • 類的代理 class
  • 屬性延遲加載 lazy
  • 監聽屬性變化 Delegates.observable ( 擴展 Delegates.vetoable )
  • 自定義監聽屬性變化 ReadWriteProperty

我們再舉個栗子,看看其如何實現屬性的代理,和上面講過的觀察者模式有些類似

class Delegate {
    // 運算符重載
    operator fun getValue(thisRef: Any?, prop: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${prop.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) {
        println("$value has been assigned to ${prop.name} in $thisRef")
    }

}

class Example {
    var d: String by Delegate()

    @Test
    fun test(){

        d = "d value"
        println("$d")

    }
}

運行結果是這樣的:


image.png

從結果看,String類型的d的取值和賦值已經交給了Delegate類去實現,想不想知道如何實現的?我們來反編譯下看看源碼吧

public final class Delegate {
   @NotNull
   public final String getValue(@Nullable Object thisRef, @NotNull KProperty prop) {
      Intrinsics.checkParameterIsNotNull(prop, "prop");
      return thisRef + ", thank you for delegating '" + prop.getName() + "' to me!";
   }

   public final void setValue(@Nullable Object thisRef, @NotNull KProperty prop, @NotNull String value) {
      Intrinsics.checkParameterIsNotNull(prop, "prop");
      Intrinsics.checkParameterIsNotNull(value, "value");
      String var4 = value + " has been assigned to " + prop.getName() + " in " + thisRef;
      boolean var5 = false;
      System.out.println(var4);
   }
}


public final class Example {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Example.class), "d", "getD()Ljava/lang/String;"))};
   @NotNull
   private final Delegate d$delegate = new Delegate();

   @NotNull
   public final String getD() {
      return this.d$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public final void setD(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.d$delegate.setValue(this, $$delegatedProperties[0], var1);
   }

   @Test
   public final void test() {
      this.setD("d value");
      String var1 = String.valueOf(this.getD());
      boolean var2 = false;
      System.out.println(var1);
   }
}

可以看到,Example中給d取值和賦值實際上調用的是getD和setD方法,而且最終轉而被Delegate對象代理,調用的是它的getValue和setValue方法,這個其實就是代理模式中by關鍵字的作用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。