mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-08-04 15:01:46 +00:00
fmr code
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
在Go語言中還存在着另外一種類型:接口類型。接口類型是一種抽象的類型。它不會暴露出它所代表的對象的內部值的結構和這個對象支持的基礎操作的集合;它們隻會展示出它們自己的方法。也就是説當你有看到一個接口類型的值時,你不知道它是什麽,唯一知道的就是可以通過它的方法來做什麽。
|
||||
|
||||
在本書中,我們一直使用兩個相似的函數來進行字符串的格式化:fmt.Printf它會把結果寫到標準輸出和fmt.Sprintf它會把結果以字符串的形式返迴。得益於使用接口,我們不必可悲的因爲返迴結果在使用方式上的一些淺顯不同就必需把格式化這個最睏難的過程複製一份。實際上,這兩個函數都使用了另一個函數fmt.Fprintf來進行封裝。fmt.Fprintf這個函數對它的計算結果會被怎麽使用是完全不知道的。
|
||||
|
||||
``` go
|
||||
package fmt
|
||||
func Fprintf(w io.Writer, format string, args ...interface{}) (int, error)
|
||||
@@ -17,10 +18,12 @@ func Sprintf(format string, args ...interface{}) string {
|
||||
return buf.String()
|
||||
}
|
||||
```
|
||||
|
||||
Fprintf的前綴F表示文件(File)也表明格式化輸出結果應該被寫入第一個參數提供的文件中。在Printf函數中的第一個參數os.Stdout是*os.File類型;在Sprintf函數中的第一個參數&buf是一個指向可以寫入字節的內存緩衝區,然而它
|
||||
併不是一個文件類型盡管它在某種意義上和文件類型相似。
|
||||
|
||||
卽使Fprintf函數中的第一個參數也不是一個文件類型。它是io.Writer類型這是一個接口類型定義如下:
|
||||
|
||||
``` go
|
||||
package io
|
||||
// Writer is the interface that wraps the basic Write method.
|
||||
@@ -41,6 +44,7 @@ io.Writer類型定義了函數Fprintf和這個函數調用者之間的約定。
|
||||
因爲fmt.Fprintf函數沒有對具體操作的值做任何假設而是僅僅通過io.Writer接口的約定來保證行爲,所以第一個參數可以安全地傳入一個任何具體類型的值隻需要滿足io.Writer接口。一個類型可以自由的使用另一個滿足相同接口的類型來進行替換被稱作可替換性(LSP里氏替換)。這是一個面向對象的特徵。
|
||||
|
||||
讓我們通過一個新的類型來進行校驗,下面\*ByteCounter類型里的Write方法,僅僅在丟失寫向它的字節前統計它們的長度。(在這個+=賦值語句中,讓len(p)的類型和\*c的類型匹配的轉換是必鬚的。)
|
||||
|
||||
```go
|
||||
// gopl.io/ch7/bytecounter
|
||||
type ByteCounter int
|
||||
@@ -50,6 +54,7 @@ func (c *ByteCounter) Write(p []byte) (int, error) {
|
||||
}
|
||||
```
|
||||
因爲*ByteCounter滿足io.Writer的約定,我們可以把它傳入Fprintf函數中;Fprintf函數執行字符串格式化的過程不會去關註ByteCounter正確的纍加結果的長度。
|
||||
|
||||
```go
|
||||
var c ByteCounter
|
||||
c.Write([]byte("hello"))
|
||||
@@ -59,7 +64,9 @@ var name = "Dolly"
|
||||
fmt.Fprintf(&c, "hello, %s", name)
|
||||
fmt.Println(c) // "12", = len("hello, Dolly")
|
||||
```
|
||||
|
||||
除了io.Writer這個接口類型,還有另一個對fmt包很重要的接口類型。Fprintf和Fprintln函數向類型提供了一種控製它們值輸出的途徑。在2.5節中,我們爲Celsius類型提供了一個String方法以便於可以打印成這樣"100°C" ,在6.5節中我們給*IntSet添加一個String方法,這樣集合可以用傳統的符號來進行表示就像"{1 2 3}"。給一個類型定義String方法,可以讓它滿足最廣泛使用之一的接口類型fmt.Stringer:
|
||||
|
||||
```go
|
||||
package fmt
|
||||
// The String method is used to print values passed
|
||||
@@ -69,12 +76,15 @@ type Stringer interface {
|
||||
String() string
|
||||
}
|
||||
```
|
||||
|
||||
我們會在7.10節解釋fmt包怎麽發現哪些值是滿足這個接口類型的。
|
||||
|
||||
練習7.1:使用來自ByteCounter的思路,實現一個針對對單詞和行數的計數器。你會發現bufio.ScanWords非常的有用。
|
||||
|
||||
練習7.2:寫一個帶有如下函數籤名的函數CountingWriter,傳入一個io.Writer接口類型,返迴一個新的Writer類型把原來的Writer封裝在里面和一個表示寫入新的Writer字節數的int64類型指針
|
||||
|
||||
```go
|
||||
func CountingWriter(w io.Writer) (io.Writer, *int64)
|
||||
```
|
||||
|
||||
練習7.3:爲在gopl.io/ch4/treesort (§4.4)的*tree類型實現一個String方法去展示tree類型的值序列。
|
||||
|
@@ -2,6 +2,7 @@
|
||||
接口類型具體描述了一繫列方法的集合,一個實現了這些方法的具體類型是這個接口類型的實例。
|
||||
|
||||
io.Writer類型是用的最廣泛的接口之一,因爲它提供了所有的類型寫入bytes的抽象,包括文件類型,內存緩衝區,網絡鏈接,HTTP客戶端,壓縮工具,哈希等等。io包中定義了很多其它有用的接口類型。Reader可以代表任意可以讀取bytes的類型,Closer可以是任意可以關閉的值,例如一個文件或是網絡鏈接。(到現在你可能註意到了很多Go語言中單方法接口的命名習慣)
|
||||
|
||||
```go
|
||||
package io
|
||||
type Reader interface {
|
||||
@@ -11,9 +12,10 @@ type Closer interface {
|
||||
Close() error
|
||||
}
|
||||
```
|
||||
在往下看,我們發現有些新的接口類型通過組合已經有的接口來定義。下面是兩個例子:
|
||||
```go
|
||||
|
||||
在往下看,我們發現有些新的接口類型通過組合已經有的接口來定義。下面是兩個例子:
|
||||
|
||||
```go
|
||||
type ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
@@ -25,24 +27,29 @@ type ReadWriteCloser interface {
|
||||
}
|
||||
```
|
||||
上面用到的語法和結構內嵌相似,我們可以用這種方式以一個簡寫命名另一個接口,而不用聲明它所有的方法。這種方式本稱爲接口內嵌。盡管略失簡潔,我們可以像下面這樣,不使用內嵌來聲明io.Writer接口。
|
||||
|
||||
```go
|
||||
type ReadWriter interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
Write(p []byte) (n int, err error)
|
||||
}
|
||||
```
|
||||
|
||||
或者甚至使用種混合的風格:
|
||||
|
||||
```go
|
||||
type ReadWriter interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
Writer
|
||||
}
|
||||
```
|
||||
|
||||
上面3種定義方式都是一樣的效果。方法的順序變化也沒有影響,唯一重要的就是這個集合里面的方法。
|
||||
|
||||
練習7.4:strings.NewReader函數通過讀取一個string參數返迴一個滿足io.Reader接口類型的值(和其它值)。實現一個簡單版本的NewReader,併用它來構造一個接收字符串輸入的HTML解析器(§5.2)
|
||||
|
||||
練習7.5:io包里面的LimitReader函數接收一個io.Reader接口類型的r和字節數n,併且返迴另一個從r中讀取字節但是當讀完n個字節後就表示讀到文件結束的Reader。實現這個LimitReader函數:
|
||||
|
||||
```go
|
||||
func LimitReader(r io.Reader, n int64) io.Reader
|
||||
```
|
||||
|
Reference in New Issue
Block a user