mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-11-02 02:52:10 +00:00
回到简体
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
## 7.1. 接口約定
|
||||
## 7.1. 接口约定
|
||||
|
||||
目前爲止,我們看到的類型都是具體的類型。一個具體的類型可以準確的描述它所代表的值併且展示出對類型本身的一些操作方式就像數字類型的算術操作,切片類型的索引、附加和取范圍操作。具體的類型還可以通過它的方法提供額外的行爲操作。總的來説,當你拿到一個具體的類型時你就知道它的本身是什麽和你可以用它來做什麽。
|
||||
目前为止,我们看到的类型都是具体的类型。一个具体的类型可以准确的描述它所代表的值并且展示出对类型本身的一些操作方式就像数字类型的算术操作,切片类型的索引、附加和取范围操作。具体的类型还可以通过它的方法提供额外的行为操作。总的来说,当你拿到一个具体的类型时你就知道它的本身是什么和你可以用它来做什么。
|
||||
|
||||
在Go語言中還存在着另外一種類型:接口類型。接口類型是一種抽象的類型。它不會暴露出它所代表的對象的內部值的結構和這個對象支持的基礎操作的集合;它們隻會展示出它們自己的方法。也就是説當你有看到一個接口類型的值時,你不知道它是什麽,唯一知道的就是可以通過它的方法來做什麽。
|
||||
在Go语言中还存在着另外一种类型:接口类型。接口类型是一种抽象的类型。它不会暴露出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会展示出它们自己的方法。也就是说当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的方法来做什么。
|
||||
|
||||
在本書中,我們一直使用兩個相似的函數來進行字符串的格式化:fmt.Printf它會把結果寫到標準輸出和fmt.Sprintf它會把結果以字符串的形式返迴。得益於使用接口,我們不必可悲的因爲返迴結果在使用方式上的一些淺顯不同就必需把格式化這個最睏難的過程複製一份。實際上,這兩個函數都使用了另一個函數fmt.Fprintf來進行封裝。fmt.Fprintf這個函數對它的計算結果會被怎麽使用是完全不知道的。
|
||||
在本书中,我们一直使用两个相似的函数来进行字符串的格式化:fmt.Printf它会把结果写到标准输出和fmt.Sprintf它会把结果以字符串的形式返回。得益于使用接口,我们不必可悲的因为返回结果在使用方式上的一些浅显不同就必需把格式化这个最困难的过程复制一份。实际上,这两个函数都使用了另一个函数fmt.Fprintf来进行封装。fmt.Fprintf这个函数对它的计算结果会被怎么使用是完全不知道的。
|
||||
|
||||
``` go
|
||||
package fmt
|
||||
@@ -20,10 +20,10 @@ func Sprintf(format string, args ...interface{}) string {
|
||||
}
|
||||
```
|
||||
|
||||
Fprintf的前綴F表示文件(File)也表明格式化輸出結果應該被寫入第一個參數提供的文件中。在Printf函數中的第一個參數os.Stdout是*os.File類型;在Sprintf函數中的第一個參數&buf是一個指向可以寫入字節的內存緩衝區,然而它
|
||||
併不是一個文件類型盡管它在某種意義上和文件類型相似。
|
||||
Fprintf的前缀F表示文件(File)也表明格式化输出结果应该被写入第一个参数提供的文件中。在Printf函数中的第一个参数os.Stdout是*os.File类型;在Sprintf函数中的第一个参数&buf是一个指向可以写入字节的内存缓冲区,然而它
|
||||
并不是一个文件类型尽管它在某种意义上和文件类型相似。
|
||||
|
||||
卽使Fprintf函數中的第一個參數也不是一個文件類型。它是io.Writer類型這是一個接口類型定義如下:
|
||||
即使Fprintf函数中的第一个参数也不是一个文件类型。它是io.Writer类型这是一个接口类型定义如下:
|
||||
|
||||
``` go
|
||||
package io
|
||||
@@ -41,11 +41,11 @@ type Writer interface {
|
||||
}
|
||||
```
|
||||
|
||||
io.Writer類型定義了函數Fprintf和這個函數調用者之間的約定。一方面這個約定需要調用者提供具體類型的值就像\*os.File和\*bytes.Buffer,這些類型都有一個特定籤名和行爲的Write的函數。另一方面這個約定保證了Fprintf接受任何滿足io.Writer接口的值都可以工作。Fprintf函數可能沒有假定寫入的是一個文件或是一段內存,而是寫入一個可以調用Write函數的值。
|
||||
io.Writer类型定义了函数Fprintf和这个函数调用者之间的约定。一方面这个约定需要调用者提供具体类型的值就像\*os.File和\*bytes.Buffer,这些类型都有一个特定签名和行为的Write的函数。另一方面这个约定保证了Fprintf接受任何满足io.Writer接口的值都可以工作。Fprintf函数可能没有假定写入的是一个文件或是一段内存,而是写入一个可以调用Write函数的值。
|
||||
|
||||
因爲fmt.Fprintf函數沒有對具體操作的值做任何假設而是僅僅通過io.Writer接口的約定來保證行爲,所以第一個參數可以安全地傳入一個任何具體類型的值隻需要滿足io.Writer接口。一個類型可以自由的使用另一個滿足相同接口的類型來進行替換被稱作可替換性(LSP里氏替換)。這是一個面向對象的特徵。
|
||||
因为fmt.Fprintf函数没有对具体操作的值做任何假设而是仅仅通过io.Writer接口的约定来保证行为,所以第一个参数可以安全地传入一个任何具体类型的值只需要满足io.Writer接口。一个类型可以自由的使用另一个满足相同接口的类型来进行替换被称作可替换性(LSP里氏替换)。这是一个面向对象的特征。
|
||||
|
||||
讓我們通過一個新的類型來進行校驗,下面\*ByteCounter類型里的Write方法,僅僅在丟失寫向它的字節前統計它們的長度。(在這個+=賦值語句中,讓len(p)的類型和\*c的類型匹配的轉換是必須的。)
|
||||
让我们通过一个新的类型来进行校验,下面\*ByteCounter类型里的Write方法,仅仅在丢失写向它的字节前统计它们的长度。(在这个+=赋值语句中,让len(p)的类型和\*c的类型匹配的转换是必须的。)
|
||||
|
||||
<u><i>gopl.io/ch7/bytecounter</i></u>
|
||||
```go
|
||||
@@ -57,7 +57,7 @@ func (c *ByteCounter) Write(p []byte) (int, error) {
|
||||
}
|
||||
```
|
||||
|
||||
因爲*ByteCounter滿足io.Writer的約定,我們可以把它傳入Fprintf函數中;Fprintf函數執行字符串格式化的過程不會去關註ByteCounter正確的纍加結果的長度。
|
||||
因为*ByteCounter满足io.Writer的约定,我们可以把它传入Fprintf函数中;Fprintf函数执行字符串格式化的过程不会去关注ByteCounter正确的累加结果的长度。
|
||||
|
||||
```go
|
||||
var c ByteCounter
|
||||
@@ -69,7 +69,7 @@ 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:
|
||||
除了io.Writer这个接口类型,还有另一个对fmt包很重要的接口类型。Fprintf和Fprintln函数向类型提供了一种控制它们值输出的途径。在2.5节中,我们为Celsius类型提供了一个String方法以便于可以打印成这样"100°C" ,在6.5节中我们给*IntSet添加一个String方法,这样集合可以用传统的符号来进行表示就像"{1 2 3}"。给一个类型定义String方法,可以让它满足最广泛使用之一的接口类型fmt.Stringer:
|
||||
|
||||
```go
|
||||
package fmt
|
||||
@@ -82,14 +82,14 @@ type Stringer interface {
|
||||
}
|
||||
```
|
||||
|
||||
我們會在7.10節解釋fmt包怎麽發現哪些值是滿足這個接口類型的。
|
||||
我们会在7.10节解释fmt包怎么发现哪些值是满足这个接口类型的。
|
||||
|
||||
**練習 7.1:** 使用來自ByteCounter的思路,實現一個針對對單詞和行數的計數器。你會發現bufio.ScanWords非常的有用。
|
||||
**练习 7.1:** 使用来自ByteCounter的思路,实现一个针对对单词和行数的计数器。你会发现bufio.ScanWords非常的有用。
|
||||
|
||||
**練習 7.2:** 寫一個帶有如下函數籤名的函數CountingWriter,傳入一個io.Writer接口類型,返迴一個新的Writer類型把原來的Writer封裝在里面和一個表示寫入新的Writer字節數的int64類型指針
|
||||
**练习 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類型的值序列。
|
||||
**练习 7.3:** 为在gopl.io/ch4/treesort (§4.4)的*tree类型实现一个String方法去展示tree类型的值序列。
|
||||
|
||||
Reference in New Issue
Block a user