前言
這個名字不知道取得是否合適,簡單來說要乾的事情就是給某個類型添加一些擴展方法,此場景在各種語言中都會用到,比如 C# 語言,如果我們使用一個別人寫好的類庫,而又想給某個類庫添加一些自己封裝的方法,最好的方式就是使用擴展方法,具體實現方式此處不贅述。
起初,我以爲在 Scala 中也是這樣使用的,但是直到今天我才恍然大悟,在 Scala 中擴展方法其實不是那麼簡單,此處說的不簡單主要說的是實現的意義不簡單,而不是實現方法。本文對此進行簡單介紹。
一、 實現方法
首先,來說明實現方法,正如上文所說,在 Scala 中其實實現起來也很容易。
首先我們有一個要擴展的類型假定爲 C,定義如下:
trait A {
def play = println("play")
}
就是這麼簡單的一個類,包含一個 play 方法,當然可以有各種各樣的子類繼承於他,及包含其他方法。
第二步,定義一個擴展方法的類型:
trait BB[+T] {
def self: T
}
此類用於包裝我們的被擴展類型,其中 self 就是一個要擴展類型的實例。(此處名字取 BB 實非本意,不知是 scala bug 還是其他問題,如果此處只使用 B 來命名,下面會報錯,有知情者煩請指點一二。)
第三步,定義一個 trait 繼承自 BB:
trait C extends BB[A] {
def draw = self.play
}
此類型裏可以定義一系列的方法,這些方法就是即將被擴展的方法,我們可以直接使用 self 來表示被擴展的對象實例,可以直接調用他的方法。
第四步,實現一個隱式類型,將 A 對象隱式轉換爲 C:
implicit class D(val self: A) extends C
最終,我們可以直接對 A 對象的實例調用擴展方法:
new A {}.draw
二、分析
思路縝密,邏輯清晰((*^_^*)),實現起來很容易。開始的時候我以爲就是這樣,所以爲每個類型寫了一堆的擴展方法,自我感覺良好,調用起來很方便。
但是這個地方有個問題,既然我們自己定義了類型A,爲什麼不直接將 draw 方法也寫到這裏面呢,瞎折騰啥,我之前是這麼想的,也是這麼用的。然而,今天我突然意識到一個問題,在我使用的一個類庫中,很多自己定義的類型也採用此種方式定義了大量的擴展方法,那我就納悶了,爲什麼不直接寫到類型的定義中呢?
沉思半天,我恍然大悟,這是一種良好的封裝方法。簡單說來就是 A 這個類型可能包含了大量的不同種類的方法,比如對於地理信息系統來說,一個瓦片可以包含投影、裁剪、切割等多種種類的方法,每個種類可能包含了一系列方法,所以採用這種方式的好處就是可以將這些不同種類的方法放到不同的擴展類型中進行管理,實現良好的封裝。並且在後續調用中,無需判斷此對象是否是 A 對象,只要判斷此對象是否是 C 對象即可直接調用 C 中的方法,這樣就對功能實現了良好的劃分。
三、結束
看似一個簡單的擴展方法,也有如此多的深層次邏輯,還是需要學會思考、深入的思考,這樣才能發現更多 coding 中美的地方。