??本文繼續(xù)玩轉(zhuǎn)語法,是為之二。
??I/O(Input/Output),輸入輸出是計算機最為突出的特點,也可以說是計算機最為核心的功能。沒有I/O,計算機就是一堆廢銅廢鐵。從最低層的電子元器件開始,計算機科學(xué)家與工程師們,就一直奔跑在追求卓越的I/O性能的道路上。計算機每一次大跨越,就是一次I/O的脫胎換骨。從機械時代到電子管,到晶體管,再到集成電路,再到未來的量子時代,無不預(yù)示著I/O對于計算機科學(xué)的重要性。但這只是最基礎(chǔ)的I/O層,在這之上,又有更多層次抽象,比如內(nèi)存、磁盤、網(wǎng)絡(luò);更多的I/O表現(xiàn)形式,比如文件、數(shù)據(jù)庫、鍵鼠、顯示器、打印機等等?!耙磺薪允俏募?,這是Unix/Linux的基本哲學(xué)之一,從這個角度看,即一切皆I/O。
??底層設(shè)計是如此,那么在高層設(shè)計,仍然是如此。從各個語言對于I/O的實現(xiàn)來看,無不將I/O置于重中之重的地方,從I/O的設(shè)計中,可大致看出一個語言的設(shè)計哲學(xué)與理念。比如java中有大量的類名包含stream,強調(diào)流的概念;而golang則沒有出現(xiàn)這個詞,更強調(diào)的是byte,即字節(jié)。從所有即文件的角度來看,字節(jié)更直觀地對應(yīng)到文件中的內(nèi)容。而java則又進行了一次抽象,相當(dāng)于將很多的字節(jié)看作是連續(xù)的字節(jié)流,同時對流提供了更多的操作方法,如flip、mark等。
??在Java中,I/O相關(guān)的SDK使用了大量的裝飾器與適配器模式。裝飾器模式其實是對原有類作功能增強的一種方法,而適配器則是將非同類型的東西變?yōu)橥愋偷臇|西。比如StringReader即是適配器類,它實現(xiàn)了流對象接口Reader。它的功能是將非流對象String適配為流對象。舊版SDK還有一個叫StringInputStream的類,它也是適配器類。適配器與裝飾器詳解如下:
|項|用途|參與對象|jdk示例|
|:—|:—|:—|
|裝飾器|功能增強|兩個或以上相同類型的類|BufferedInputStream, 可用于裝飾其他的InputStream子類|
|適配器|類型轉(zhuǎn)換(適配)|兩個不同類型的類|StringReader, 將String轉(zhuǎn)換(適配)為Reader類型|
??而在go語言中,I/O也大量采用這樣的設(shè)計方式。比如:
項 | 相關(guān)類 | 示例 |
---|---|---|
裝飾器 | MultiReader, MultiWriter, bufio.Reader, bufio.Writer, bufio.Scanner, LimitReader, TeeReader | - |
適配器 | string.Reader, bytes.Reader | ex: strings.NewReader(“test”) |
??裝飾器與適配器的使用,使得go庫的設(shè)計大為簡化,并且因此而產(chǎn)生無比的靈活性與擴展性,開發(fā)者可以自行設(shè)計新的裝飾器、適配器完成開發(fā)任務(wù)。
??那么,在實際的開發(fā)過程中,裝飾器與適配器還有哪些妙用呢?在前面講述的例子中,這兩者是應(yīng)用在interface與struct的層面,其實在go語言中,兩者更妙的應(yīng)用是與func的結(jié)合,可以帶來新鮮、意想不到的體驗。比如:
package mainimport "fmt"func greet(name string) { fmt.Printf("Hello %s!\n", name)}func decorateGreet(f func(string), name string) { fmt.Println("before greet") f(name) fmt.Println("after greet")}func main() { decorateGreet(greet, "John")}
??雖然這個例子有點丑陋,但這真的是裝飾器的應(yīng)用。盡管java的經(jīng)驗告訴我們,面向切面編程使用的應(yīng)該是代理模式(靜態(tài)代理或者動態(tài)代理),甚至要動用反射、ASM,但其實使用裝飾器更直接。當(dāng)然為了裝一下逼,通常不會寫上面那么直白的代碼。
package mainimport "fmt"func greet(name string) { fmt.Printf("Hello %s!\n", name)}func decorateGreet(f func(string)) func(string) { return func(name string) { fmt.Println("before greet") f(name) fmt.Println("after greet") }}func main() { decorateGreet(greet)("裝逼John")}
??調(diào)用方式變了一下,檔次一下升高了不知多少。這樣看起來才能讓人相信:我真的是在用裝飾器。很顯然,裝飾器起到一個功能增強的作用,從代碼上看,無非就是接受A類型的對象(函數(shù)也是對象),然后返回A類型的對象,而在返回類型的實現(xiàn)中觸發(fā)入?yún)?,同時加一些功能增強的代碼。
??對于適配器來說,使用它想當(dāng)于做了一次類型強轉(zhuǎn),如下面的示例嘗試將hi函數(shù)適配成greet函數(shù)。
package mainimport "fmt"type hi func()type greet func(string)func sayHi() { fmt.Printf("Hi")}func toGreet(h hi) greet { return func(name string) { h() fmt.Printf(", %s", name) }}func main() { toGreet(sayHi)("John")}
??當(dāng)函數(shù)是一等公民的時候,一切看起來都是那么地自然順暢,而類似的功能用java來實現(xiàn),就必須提高到類的層面了。
歡迎關(guān)注個人公眾號:不一樣的go語言
聯(lián)系客服