Prechádzať zdrojové kódy

改正错别字及微调格式 (#782)

SiHuan 5 rokov pred
rodič
commit
806880974d
43 zmenil súbory, kde vykonal 894 pridanie a 894 odobranie
  1. 1 1
      eBook/02.5.md
  2. 1 1
      eBook/02.6.md
  3. 1 1
      eBook/03.0.md
  4. 1 1
      eBook/03.3.md
  5. 2 2
      eBook/06.0.md
  6. 1 1
      eBook/06.11.md
  7. 1 1
      eBook/10.0.md
  8. 9 9
      eBook/11.0.md
  9. 256 256
      eBook/11.1.md
  10. 1 1
      eBook/11.13.md
  11. 29 29
      eBook/11.2.md
  12. 96 96
      eBook/11.3.md
  13. 77 77
      eBook/11.4.md
  14. 31 31
      eBook/11.5.md
  15. 92 92
      eBook/11.6.md
  16. 227 227
      eBook/11.7.md
  17. 29 29
      eBook/11.8.md
  18. 1 1
      eBook/12.0.md
  19. 1 1
      eBook/13.0.md
  20. 1 1
      eBook/14.0.md
  21. 1 1
      eBook/15.0.md
  22. 1 1
      eBook/15.2.md
  23. 1 1
      eBook/15.3.md
  24. 1 1
      eBook/16.0.md
  25. 1 1
      eBook/16.1.md
  26. 2 2
      eBook/16.3.md
  27. 2 2
      eBook/16.4.md
  28. 1 1
      eBook/16.7.md
  29. 1 1
      eBook/16.8.md
  30. 1 1
      eBook/16.9.md
  31. 1 1
      eBook/17.0.md
  32. 2 2
      eBook/18.0.md
  33. 1 1
      eBook/18.10.md
  34. 1 1
      eBook/18.2.md
  35. 1 1
      eBook/18.3.md
  36. 1 1
      eBook/18.4.md
  37. 1 1
      eBook/18.5.md
  38. 1 1
      eBook/18.6.md
  39. 1 1
      eBook/18.7.md
  40. 1 1
      eBook/18.8.md
  41. 1 1
      eBook/18.9.md
  42. 1 1
      eBook/19.0.md
  43. 11 11
      eBook/directory.md

+ 1 - 1
eBook/02.5.md

@@ -20,4 +20,4 @@
 
 - [目录](directory.md)
 - 上一节:[在 Mac OS X 上安装 Go](02.4.md)
-- 下一节:[安装目录清单](02.6.md)
+- 下一节:[安装目录清单](02.6.md)

+ 1 - 1
eBook/02.6.md

@@ -16,4 +16,4 @@ README.md, AUTHORS, CONTRIBUTORS, LICENSE
 
 - [目录](directory.md)
 - 上一节:[在 Windows 上安装 Go](02.5.md)
-- 下一节:[Go 运行时(runtime)](02.7.md)
+- 下一节:[Go 运行时(runtime)](02.7.md)

+ 1 - 1
eBook/03.0.md

@@ -8,4 +8,4 @@
 
 - [目录](directory.md)
 - 上一章:[Go 解释器](02.8.md)
-- 下一节:[Go 开发环境的基本要求](03.1.md)
+- 下一节:[Go 开发环境的基本要求](03.1.md)

+ 1 - 1
eBook/03.3.md

@@ -18,4 +18,4 @@
 
 - [目录](directory.md)
 - 上一节:[编辑器和集成开发环境](03.2.md)
-- 下一节:[构建并运行 Go 程序](03.4.md)
+- 下一节:[构建并运行 Go 程序](03.4.md)

+ 2 - 2
eBook/06.0.md

@@ -1,4 +1,4 @@
-# 6.0 函数
+# 6.0 函数(function)
 
 函数是 Go 里面的基本代码块:Go 函数的功能非常强大,以至于被认为拥有函数式编程语言的多种特性。在这一章,我们将对 [第 4.2.2 节](04.2.md) 所简要描述的函数进行详细的讲解。
 
@@ -6,4 +6,4 @@
 
 - [目录](directory.md)
 - 上一章:[标签与 goto](05.6.md)
-- 下一节:[介绍](06.1.md)
+- 下一节:[介绍](06.1.md)

+ 1 - 1
eBook/06.11.md

@@ -18,4 +18,4 @@ fmt.Printf("longCalculation took this amount of time: %s\n", delta)
 
 - [目录](directory.md)
 - 上一节:[使用闭包调试](06.10.md)
-- 下一节:[通过内存缓存来提升性能](06.12.md)
+- 下一节:[通过内存缓存来提升性能](06.12.md)

+ 1 - 1
eBook/10.0.md

@@ -1,4 +1,4 @@
-# 10 结构(struct)与方法(method)
+# 10.0 结构(struct)与方法(method)
 
 Go 通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型。一个带属性的结构体试图表示一个现实世界中的实体。结构体是复合类型(composite types),当需要定义一个类型,它由一系列属性组成,每个属性都有自己的类型和值的时候,就应该使用结构体,它把数据聚集在一起。然后可以访问这些数据,就好像它是一个独立实体的一部分。结构体也是值类型,因此可以通过 **new** 函数来创建。
 

+ 9 - 9
eBook/11.0.md

@@ -1,9 +1,9 @@
-# 11 接口(Interfaces)与反射(reflection)
-
-本章介绍 Go 语言中接口和反射的相关内容。
-
-## 链接
-
-- [目录](directory.md)
-- 上一章:[垃圾回收和 SetFinalizer](10.8.md)
-- 下一节:[接口是什么](11.1.md)
+# 11.0 接口(interface)与反射(reflection)
+
+本章介绍 Go 语言中接口和反射的相关内容。
+
+## 链接
+
+- [目录](directory.md)
+- 上一章:[垃圾回收和 SetFinalizer](10.8.md)
+- 下一节:[接口是什么](11.1.md)

+ 256 - 256
eBook/11.1.md

@@ -1,256 +1,256 @@
-# 11.1 接口是什么
-
-Go 语言不是一种 *“传统”* 的面向对象编程语言:它里面没有类和继承的概念。
-
-但是 Go 语言里有非常灵活的 **接口** 概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来 **说明** 对象的行为:如果谁能搞定这件事,它就可以用在这儿。
-
-接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
-
-通过如下格式定义接口:
-
-```go
-type Namer interface {
-    Method1(param_list) return_type
-    Method2(param_list) return_type
-    ...
-}
-```
-
-上面的 `Namer` 是一个 **接口类型**。
-
-(按照约定,只包含一个方法的)接口的名字由方法名加 `[e]r` 后缀组成,例如 `Printer`、`Reader`、`Writer`、`Logger`、`Converter` 等等。还有一些不常用的方式(当后缀 `er` 不合适时),比如 `Recoverable`,此时接口名以 `able` 结尾,或者以 `I` 开头(像 `.NET` 或 `Java` 中那样)。
-
-Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。
-
-不像大多数面向对象编程语言,在 Go 语言中接口可以有值,一个接口类型的变量或一个 **接口值** :`var ai Namer`,`ai` 是一个多字(multiword)数据结构,它的值是 `nil`。它本质上是一个指针,虽然不完全是一回事。指向接口值的指针是非法的,它们不仅一点用也没有,还会导致代码错误。
-
-![](images/11.1_fig11.1.jpg?raw=true)
-
-此处的方法指针表是通过运行时反射能力构建的。
-
-类型(比如结构体)实现接口方法集中的方法,每一个方法的实现说明了此方法是如何作用于该类型的:**即实现接口**,同时方法集也构成了该类型的接口。实现了 `Namer` 接口类型的变量可以赋值给 `ai` (接收者值),此时方法表中的指针会指向被实现的接口方法。当然如果另一个类型(也实现了该接口)的变量被赋值给 `ai`,这二者(译者注:指针和方法实现)也会随之改变。
-
-**类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口**。
-
-**实现某个接口的类型(除了实现接口方法外)可以有其他的方法**。
-
-**一个类型可以实现多个接口**。
-
-**接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)**。
-
-即使接口在类型之后才定义,二者处于不同的包中,被单独编译:只要类型实现了接口中的方法,它就实现了此接口。
-
-所有这些特性使得接口具有很大的灵活性。
-
-第一个例子:
-
-示例 11.1 [interfaces.go](examples/chapter_11/interfaces.go):
-
-```go
-package main
-
-import "fmt"
-
-type Shaper interface {
-	Area() float32
-}
-
-type Square struct {
-	side float32
-}
-
-func (sq *Square) Area() float32 {
-	return sq.side * sq.side
-}
-
-func main() {
-	sq1 := new(Square)
-	sq1.side = 5
-
-	var areaIntf Shaper
-	areaIntf = sq1
-	// shorter,without separate declaration:
-	// areaIntf := Shaper(sq1)
-	// or even:
-	// areaIntf := sq1
-	fmt.Printf("The square has area: %f\n", areaIntf.Area())
-}
-```
-
-输出:
-
-    The square has area: 25.000000
-
-上面的程序定义了一个结构体 `Square` 和一个接口 `Shaper`,接口有一个方法 `Area()`。
-
-在 `main()` 方法中创建了一个 `Square` 的实例。在主程序外边定义了一个接收者类型是 `Square` 方法的 `Area()`,用来计算正方形的面积:结构体 `Square` 实现了接口 `Shaper` 。
-
-所以可以将一个 `Square` 类型的变量赋值给一个接口类型的变量:`areaIntf = sq1` 。
-
-现在接口变量包含一个指向 `Square` 变量的引用,通过它可以调用 `Square` 上的方法 `Area()`。当然也可以直接在 `Square` 的实例上调用此方法,但是在接口实例上调用此方法更令人兴奋,它使此方法更具有一般性。接口变量里包含了接收者实例的值和指向对应方法表的指针。
-
-这是 **多态** 的 Go 版本,多态是面向对象编程中一个广为人知的概念:根据当前的类型选择正确的方法,或者说:同一种类型在不同的实例上似乎表现出不同的行为。
-
-如果 `Square` 没有实现 `Area()` 方法,编译器将会给出清晰的错误信息:
-
-    cannot use sq1 (type *Square) as type Shaper in assignment:
-    *Square does not implement Shaper (missing Area method)
-
-如果 `Shaper` 有另外一个方法 `Perimeter()`,但是`Square` 没有实现它,即使没有人在 `Square` 实例上调用这个方法,编译器也会给出上面同样的错误。
-
-扩展一下上面的例子,类型 `Rectangle` 也实现了 `Shaper` 接口。接着创建一个 `Shaper` 类型的数组,迭代它的每一个元素并在上面调用 `Area()` 方法,以此来展示多态行为:
-
-示例 11.2 [interfaces_poly.go](examples/chapter_11/interfaces_poly.go):
-
-```go
-package main
-
-import "fmt"
-
-type Shaper interface {
-	Area() float32
-}
-
-type Square struct {
-	side float32
-}
-
-func (sq *Square) Area() float32 {
-	return sq.side * sq.side
-}
-
-type Rectangle struct {
-	length, width float32
-}
-
-func (r Rectangle) Area() float32 {
-	return r.length * r.width
-}
-
-func main() {
-
-	r := Rectangle{5, 3} // Area() of Rectangle needs a value
-	q := &Square{5}      // Area() of Square needs a pointer
-	// shapes := []Shaper{Shaper(r), Shaper(q)}
-	// or shorter
-	shapes := []Shaper{r, q}
-	fmt.Println("Looping through shapes for area ...")
-	for n, _ := range shapes {
-		fmt.Println("Shape details: ", shapes[n])
-		fmt.Println("Area of this shape is: ", shapes[n].Area())
-	}
-}
-```
-
-输出:
-
-    Looping through shapes for area ...
-    Shape details:  {5 3}
-    Area of this shape is:  15
-    Shape details:  &{5}
-    Area of this shape is:  25
-
-在调用 `shapes[n].Area() ` 这个时,只知道 `shapes[n]` 是一个 `Shaper` 对象,最后它摇身一变成为了一个 `Square` 或 `Rectangle` 对象,并且表现出了相对应的行为。
-
-也许从现在开始你将看到通过接口如何产生 **更干净**、**更简单** 及 **更具有扩展性** 的代码。在 11.12.3 中将看到在开发中为类型添加新的接口是多么的容易。
-
-下面是一个更具体的例子:有两个类型 `stockPosition` 和 `car`,它们都有一个 `getValue()` 方法,我们可以定义一个具有此方法的接口 `valuable`。接着定义一个使用 `valuable` 类型作为参数的函数 `showValue()`,所有实现了 `valuable` 接口的类型都可以用这个函数。
-
-示例 11.3 [valuable.go](examples/chapter_11/valuable.go):
-
-```go
-package main
-
-import "fmt"
-
-type stockPosition struct {
-	ticker     string
-	sharePrice float32
-	count      float32
-}
-
-/* method to determine the value of a stock position */
-func (s stockPosition) getValue() float32 {
-	return s.sharePrice * s.count
-}
-
-type car struct {
-	make  string
-	model string
-	price float32
-}
-
-/* method to determine the value of a car */
-func (c car) getValue() float32 {
-	return c.price
-}
-
-/* contract that defines different things that have value */
-type valuable interface {
-	getValue() float32
-}
-
-func showValue(asset valuable) {
-	fmt.Printf("Value of the asset is %f\n", asset.getValue())
-}
-
-func main() {
-	var o valuable = stockPosition{"GOOG", 577.20, 4}
-	showValue(o)
-	o = car{"BMW", "M3", 66500}
-	showValue(o)
-}
-```
-
-输出:
-
-    Value of the asset is 2308.800049
-    Value of the asset is 66500.000000
-
-**一个标准库的例子**
-
-`io` 包里有一个接口类型 `Reader`:
-
-```go
-type Reader interface {
-    Read(p []byte) (n int, err error)
-}
-```
-
-定义变量 `r`:` var r io.Reader`
-
-那么就可以写如下的代码:
-
-```go
-	var r io.Reader
-	r = os.Stdin    // see 12.1
-	r = bufio.NewReader(r)
-	r = new(bytes.Buffer)
-	f,_ := os.Open("test.txt")
-	r = bufio.NewReader(f)
-```
-
-上面 `r` 右边的类型都实现了 `Read()` 方法,并且有相同的方法签名,`r` 的静态类型是 `io.Reader`。
-
-**备注**
-
-有的时候,也会以一种稍微不同的方式来使用接口这个词:从某个类型的角度来看,它的接口指的是:它的所有导出方法,只不过没有显式地为这些导出方法额外定一个接口而已。
-
-**练习 11.1** simple_interface.go:
-
-定义一个接口 `Simpler`,它有一个 `Get()` 方法和一个 `Set()`,`Get()`返回一个整型值,`Set()` 有一个整型参数。创建一个结构体类型 `Simple` 实现这个接口。
-
-接着定一个函数,它有一个 `Simpler` 类型的参数,调用参数的 `Get()` 和 `Set()` 方法。在 `main` 函数里调用这个函数,看看它是否可以正确运行。
-
-**练习 11.2** interfaces_poly2.go:
-
-a) 扩展 interfaces_poly.go 中的例子,添加一个 `Circle` 类型
-
-b) 使用一个抽象类型 `Shape`(没有字段) 实现同样的功能,它实现接口 `Shaper`,然后在其他类型里内嵌此类型。扩展 10.6.5 中的例子来说明覆写。
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[接口(Interfaces)与反射(reflection)](11.0.md)
-- 下一节:[接口嵌套接口](11.2.md)
-
+# 11.1 接口是什么
+
+Go 语言不是一种 *“传统”* 的面向对象编程语言:它里面没有类和继承的概念。
+
+但是 Go 语言里有非常灵活的 **接口** 概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来 **说明** 对象的行为:如果谁能搞定这件事,它就可以用在这儿。
+
+接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
+
+通过如下格式定义接口:
+
+```go
+type Namer interface {
+    Method1(param_list) return_type
+    Method2(param_list) return_type
+    ...
+}
+```
+
+上面的 `Namer` 是一个 **接口类型**。
+
+(按照约定,只包含一个方法的)接口的名字由方法名加 `[e]r` 后缀组成,例如 `Printer`、`Reader`、`Writer`、`Logger`、`Converter` 等等。还有一些不常用的方式(当后缀 `er` 不合适时),比如 `Recoverable`,此时接口名以 `able` 结尾,或者以 `I` 开头(像 `.NET` 或 `Java` 中那样)。
+
+Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。
+
+不像大多数面向对象编程语言,在 Go 语言中接口可以有值,一个接口类型的变量或一个 **接口值** :`var ai Namer`,`ai` 是一个多字(multiword)数据结构,它的值是 `nil`。它本质上是一个指针,虽然不完全是一回事。指向接口值的指针是非法的,它们不仅一点用也没有,还会导致代码错误。
+
+![](images/11.1_fig11.1.jpg?raw=true)
+
+此处的方法指针表是通过运行时反射能力构建的。
+
+类型(比如结构体)实现接口方法集中的方法,每一个方法的实现说明了此方法是如何作用于该类型的:**即实现接口**,同时方法集也构成了该类型的接口。实现了 `Namer` 接口类型的变量可以赋值给 `ai` (接收者值),此时方法表中的指针会指向被实现的接口方法。当然如果另一个类型(也实现了该接口)的变量被赋值给 `ai`,这二者(译者注:指针和方法实现)也会随之改变。
+
+**类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口**。
+
+**实现某个接口的类型(除了实现接口方法外)可以有其他的方法**。
+
+**一个类型可以实现多个接口**。
+
+**接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)**。
+
+即使接口在类型之后才定义,二者处于不同的包中,被单独编译:只要类型实现了接口中的方法,它就实现了此接口。
+
+所有这些特性使得接口具有很大的灵活性。
+
+第一个例子:
+
+示例 11.1 [interfaces.go](examples/chapter_11/interfaces.go):
+
+```go
+package main
+
+import "fmt"
+
+type Shaper interface {
+	Area() float32
+}
+
+type Square struct {
+	side float32
+}
+
+func (sq *Square) Area() float32 {
+	return sq.side * sq.side
+}
+
+func main() {
+	sq1 := new(Square)
+	sq1.side = 5
+
+	var areaIntf Shaper
+	areaIntf = sq1
+	// shorter,without separate declaration:
+	// areaIntf := Shaper(sq1)
+	// or even:
+	// areaIntf := sq1
+	fmt.Printf("The square has area: %f\n", areaIntf.Area())
+}
+```
+
+输出:
+
+    The square has area: 25.000000
+
+上面的程序定义了一个结构体 `Square` 和一个接口 `Shaper`,接口有一个方法 `Area()`。
+
+在 `main()` 方法中创建了一个 `Square` 的实例。在主程序外边定义了一个接收者类型是 `Square` 方法的 `Area()`,用来计算正方形的面积:结构体 `Square` 实现了接口 `Shaper` 。
+
+所以可以将一个 `Square` 类型的变量赋值给一个接口类型的变量:`areaIntf = sq1` 。
+
+现在接口变量包含一个指向 `Square` 变量的引用,通过它可以调用 `Square` 上的方法 `Area()`。当然也可以直接在 `Square` 的实例上调用此方法,但是在接口实例上调用此方法更令人兴奋,它使此方法更具有一般性。接口变量里包含了接收者实例的值和指向对应方法表的指针。
+
+这是 **多态** 的 Go 版本,多态是面向对象编程中一个广为人知的概念:根据当前的类型选择正确的方法,或者说:同一种类型在不同的实例上似乎表现出不同的行为。
+
+如果 `Square` 没有实现 `Area()` 方法,编译器将会给出清晰的错误信息:
+
+    cannot use sq1 (type *Square) as type Shaper in assignment:
+    *Square does not implement Shaper (missing Area method)
+
+如果 `Shaper` 有另外一个方法 `Perimeter()`,但是`Square` 没有实现它,即使没有人在 `Square` 实例上调用这个方法,编译器也会给出上面同样的错误。
+
+扩展一下上面的例子,类型 `Rectangle` 也实现了 `Shaper` 接口。接着创建一个 `Shaper` 类型的数组,迭代它的每一个元素并在上面调用 `Area()` 方法,以此来展示多态行为:
+
+示例 11.2 [interfaces_poly.go](examples/chapter_11/interfaces_poly.go):
+
+```go
+package main
+
+import "fmt"
+
+type Shaper interface {
+	Area() float32
+}
+
+type Square struct {
+	side float32
+}
+
+func (sq *Square) Area() float32 {
+	return sq.side * sq.side
+}
+
+type Rectangle struct {
+	length, width float32
+}
+
+func (r Rectangle) Area() float32 {
+	return r.length * r.width
+}
+
+func main() {
+
+	r := Rectangle{5, 3} // Area() of Rectangle needs a value
+	q := &Square{5}      // Area() of Square needs a pointer
+	// shapes := []Shaper{Shaper(r), Shaper(q)}
+	// or shorter
+	shapes := []Shaper{r, q}
+	fmt.Println("Looping through shapes for area ...")
+	for n, _ := range shapes {
+		fmt.Println("Shape details: ", shapes[n])
+		fmt.Println("Area of this shape is: ", shapes[n].Area())
+	}
+}
+```
+
+输出:
+
+    Looping through shapes for area ...
+    Shape details:  {5 3}
+    Area of this shape is:  15
+    Shape details:  &{5}
+    Area of this shape is:  25
+
+在调用 `shapes[n].Area() ` 这个时,只知道 `shapes[n]` 是一个 `Shaper` 对象,最后它摇身一变成为了一个 `Square` 或 `Rectangle` 对象,并且表现出了相对应的行为。
+
+也许从现在开始你将看到通过接口如何产生 **更干净**、**更简单** 及 **更具有扩展性** 的代码。在 11.12.3 中将看到在开发中为类型添加新的接口是多么的容易。
+
+下面是一个更具体的例子:有两个类型 `stockPosition` 和 `car`,它们都有一个 `getValue()` 方法,我们可以定义一个具有此方法的接口 `valuable`。接着定义一个使用 `valuable` 类型作为参数的函数 `showValue()`,所有实现了 `valuable` 接口的类型都可以用这个函数。
+
+示例 11.3 [valuable.go](examples/chapter_11/valuable.go):
+
+```go
+package main
+
+import "fmt"
+
+type stockPosition struct {
+	ticker     string
+	sharePrice float32
+	count      float32
+}
+
+/* method to determine the value of a stock position */
+func (s stockPosition) getValue() float32 {
+	return s.sharePrice * s.count
+}
+
+type car struct {
+	make  string
+	model string
+	price float32
+}
+
+/* method to determine the value of a car */
+func (c car) getValue() float32 {
+	return c.price
+}
+
+/* contract that defines different things that have value */
+type valuable interface {
+	getValue() float32
+}
+
+func showValue(asset valuable) {
+	fmt.Printf("Value of the asset is %f\n", asset.getValue())
+}
+
+func main() {
+	var o valuable = stockPosition{"GOOG", 577.20, 4}
+	showValue(o)
+	o = car{"BMW", "M3", 66500}
+	showValue(o)
+}
+```
+
+输出:
+
+    Value of the asset is 2308.800049
+    Value of the asset is 66500.000000
+
+**一个标准库的例子**
+
+`io` 包里有一个接口类型 `Reader`:
+
+```go
+type Reader interface {
+    Read(p []byte) (n int, err error)
+}
+```
+
+定义变量 `r`:` var r io.Reader`
+
+那么就可以写如下的代码:
+
+```go
+	var r io.Reader
+	r = os.Stdin    // see 12.1
+	r = bufio.NewReader(r)
+	r = new(bytes.Buffer)
+	f,_ := os.Open("test.txt")
+	r = bufio.NewReader(f)
+```
+
+上面 `r` 右边的类型都实现了 `Read()` 方法,并且有相同的方法签名,`r` 的静态类型是 `io.Reader`。
+
+**备注**
+
+有的时候,也会以一种稍微不同的方式来使用接口这个词:从某个类型的角度来看,它的接口指的是:它的所有导出方法,只不过没有显式地为这些导出方法额外定一个接口而已。
+
+**练习 11.1** simple_interface.go:
+
+定义一个接口 `Simpler`,它有一个 `Get()` 方法和一个 `Set()`,`Get()`返回一个整型值,`Set()` 有一个整型参数。创建一个结构体类型 `Simple` 实现这个接口。
+
+接着定一个函数,它有一个 `Simpler` 类型的参数,调用参数的 `Get()` 和 `Set()` 方法。在 `main` 函数里调用这个函数,看看它是否可以正确运行。
+
+**练习 11.2** interfaces_poly2.go:
+
+a) 扩展 interfaces_poly.go 中的例子,添加一个 `Circle` 类型
+
+b) 使用一个抽象类型 `Shape`(没有字段) 实现同样的功能,它实现接口 `Shaper`,然后在其他类型里内嵌此类型。扩展 10.6.5 中的例子来说明覆写。
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[接口(Interfaces)与反射(reflection)](11.0.md)
+- 下一节:[接口嵌套接口](11.2.md)
+

+ 1 - 1
eBook/11.13.md

@@ -1,4 +1,4 @@
-# 总结:Go 中的面向对象
+# 11.13 总结:Go 中的面向对象
 
 我们总结一下前面看到的:Go 没有类,而是松耦合的类型、方法对接口的实现。
 

+ 29 - 29
eBook/11.2.md

@@ -1,29 +1,29 @@
-# 11.2 接口嵌套接口
-
-一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。
-
-比如接口 `File` 包含了 `ReadWrite` 和 `Lock` 的所有方法,它还额外有一个 `Close()` 方法。
-
-```go
-type ReadWrite interface {
-    Read(b Buffer) bool
-    Write(b Buffer) bool
-}
-
-type Lock interface {
-    Lock()
-    Unlock()
-}
-
-type File interface {
-    ReadWrite
-    Lock
-    Close()
-}
-```
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[接口是什么](11.1.md)
-- 下一节:[如何检测和转换接口变量的类型:类型断言](11.3.md)
+# 11.2 接口嵌套接口
+
+一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。
+
+比如接口 `File` 包含了 `ReadWrite` 和 `Lock` 的所有方法,它还额外有一个 `Close()` 方法。
+
+```go
+type ReadWrite interface {
+    Read(b Buffer) bool
+    Write(b Buffer) bool
+}
+
+type Lock interface {
+    Lock()
+    Unlock()
+}
+
+type File interface {
+    ReadWrite
+    Lock
+    Close()
+}
+```
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[接口是什么](11.1.md)
+- 下一节:[如何检测和转换接口变量的类型:类型断言](11.3.md)

+ 96 - 96
eBook/11.3.md

@@ -1,96 +1,96 @@
-# 11.3 类型断言:如何检测和转换接口变量的类型
-
-一个接口类型的变量 `varI` 中可以包含任何类型的值,必须有一种方式来检测它的 **动态** 类型,即运行时在变量中存储的值的实际类型。在执行过程中动态类型可能会有所不同,但是它总是可以分配给接口变量本身的类型。通常我们可以使用 **类型断言** 来测试在某个时刻 `varI` 是否包含类型 `T` 的值:
-
-```go
-v := varI.(T)       // unchecked type assertion
-```
-
-**varI 必须是一个接口变量**,否则编译器会报错:`invalid type assertion: varI.(T) (non-interface type (type of varI) on left)` 。
-
-类型断言可能是无效的,虽然编译器会尽力检查转换是否有效,但是它不可能预见所有的可能性。如果转换在程序运行时失败会导致错误发生。更安全的方式是使用以下形式来进行类型断言:
-
-```go
-if v, ok := varI.(T); ok {  // checked type assertion
-    Process(v)
-    return
-}
-// varI is not of type T
-```
-
-如果转换合法,`v` 是 `varI` 转换到类型 `T` 的值,`ok` 会是 `true`;否则 `v` 是类型 `T` 的零值,`ok` 是 `false`,也没有运行时错误发生。
-
-**应该总是使用上面的方式来进行类型断言**。
-
-多数情况下,我们可能只是想在 `if` 中测试一下 `ok` 的值,此时使用以下的方法会是最方便的:
-
-```go
-if _, ok := varI.(T); ok {
-    // ...
-}
-```
-
-示例 11.4 [type_interfaces.go](examples/chapter_11/type_interfaces.go):
-
-```go
-package main
-
-import (
-	"fmt"
-	"math"
-)
-
-type Square struct {
-	side float32
-}
-
-type Circle struct {
-	radius float32
-}
-
-type Shaper interface {
-	Area() float32
-}
-
-func main() {
-	var areaIntf Shaper
-	sq1 := new(Square)
-	sq1.side = 5
-
-	areaIntf = sq1
-	// Is Square the type of areaIntf?
-	if t, ok := areaIntf.(*Square); ok {
-		fmt.Printf("The type of areaIntf is: %T\n", t)
-	}
-	if u, ok := areaIntf.(*Circle); ok {
-		fmt.Printf("The type of areaIntf is: %T\n", u)
-	} else {
-		fmt.Println("areaIntf does not contain a variable of type Circle")
-	}
-}
-
-func (sq *Square) Area() float32 {
-	return sq.side * sq.side
-}
-
-func (ci *Circle) Area() float32 {
-	return ci.radius * ci.radius * math.Pi
-}
-```
-
-输出:
-
-    The type of areaIntf is: *main.Square
-    areaIntf does not contain a variable of type Circle
-
-程序中定义了一个新类型 `Circle`,它也实现了 `Shaper` 接口。 `if t, ok := areaIntf.(*Square); ok ` 测试 `areaIntf` 里是否有一个包含 `*Square` 类型的变量,结果是确定的;然后我们测试它是否包含一个 `*Circle` 类型的变量,结果是否定的。
-
-**备注**
-
-如果忽略 `areaIntf.(*Square)` 中的 `*` 号,会导致编译错误:`impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)`。
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[接口嵌套接口](11.2.md)
-- 下一节:[类型判断:type-switch](11.4.md)
+# 11.3 类型断言:如何检测和转换接口变量的类型
+
+一个接口类型的变量 `varI` 中可以包含任何类型的值,必须有一种方式来检测它的 **动态** 类型,即运行时在变量中存储的值的实际类型。在执行过程中动态类型可能会有所不同,但是它总是可以分配给接口变量本身的类型。通常我们可以使用 **类型断言** 来测试在某个时刻 `varI` 是否包含类型 `T` 的值:
+
+```go
+v := varI.(T)       // unchecked type assertion
+```
+
+**varI 必须是一个接口变量**,否则编译器会报错:`invalid type assertion: varI.(T) (non-interface type (type of varI) on left)` 。
+
+类型断言可能是无效的,虽然编译器会尽力检查转换是否有效,但是它不可能预见所有的可能性。如果转换在程序运行时失败会导致错误发生。更安全的方式是使用以下形式来进行类型断言:
+
+```go
+if v, ok := varI.(T); ok {  // checked type assertion
+    Process(v)
+    return
+}
+// varI is not of type T
+```
+
+如果转换合法,`v` 是 `varI` 转换到类型 `T` 的值,`ok` 会是 `true`;否则 `v` 是类型 `T` 的零值,`ok` 是 `false`,也没有运行时错误发生。
+
+**应该总是使用上面的方式来进行类型断言**。
+
+多数情况下,我们可能只是想在 `if` 中测试一下 `ok` 的值,此时使用以下的方法会是最方便的:
+
+```go
+if _, ok := varI.(T); ok {
+    // ...
+}
+```
+
+示例 11.4 [type_interfaces.go](examples/chapter_11/type_interfaces.go):
+
+```go
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type Square struct {
+	side float32
+}
+
+type Circle struct {
+	radius float32
+}
+
+type Shaper interface {
+	Area() float32
+}
+
+func main() {
+	var areaIntf Shaper
+	sq1 := new(Square)
+	sq1.side = 5
+
+	areaIntf = sq1
+	// Is Square the type of areaIntf?
+	if t, ok := areaIntf.(*Square); ok {
+		fmt.Printf("The type of areaIntf is: %T\n", t)
+	}
+	if u, ok := areaIntf.(*Circle); ok {
+		fmt.Printf("The type of areaIntf is: %T\n", u)
+	} else {
+		fmt.Println("areaIntf does not contain a variable of type Circle")
+	}
+}
+
+func (sq *Square) Area() float32 {
+	return sq.side * sq.side
+}
+
+func (ci *Circle) Area() float32 {
+	return ci.radius * ci.radius * math.Pi
+}
+```
+
+输出:
+
+    The type of areaIntf is: *main.Square
+    areaIntf does not contain a variable of type Circle
+
+程序中定义了一个新类型 `Circle`,它也实现了 `Shaper` 接口。 `if t, ok := areaIntf.(*Square); ok ` 测试 `areaIntf` 里是否有一个包含 `*Square` 类型的变量,结果是确定的;然后我们测试它是否包含一个 `*Circle` 类型的变量,结果是否定的。
+
+**备注**
+
+如果忽略 `areaIntf.(*Square)` 中的 `*` 号,会导致编译错误:`impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)`。
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[接口嵌套接口](11.2.md)
+- 下一节:[类型判断:type-switch](11.4.md)

+ 77 - 77
eBook/11.4.md

@@ -1,77 +1,77 @@
-# 11.4 类型判断:type-switch
-
-接口变量的类型也可以使用一种特殊形式的 `switch` 来检测:**type-switch** (下面是示例 11.4 的第二部分):
-
-```go
-switch t := areaIntf.(type) {
-case *Square:
-	fmt.Printf("Type Square %T with value %v\n", t, t)
-case *Circle:
-	fmt.Printf("Type Circle %T with value %v\n", t, t)
-case nil:
-	fmt.Printf("nil value: nothing to check?\n")
-default:
-	fmt.Printf("Unexpected type %T\n", t)
-}
-```
-
-输出:
-
-    Type Square *main.Square with value &{5}
-
-变量 `t` 得到了 `areaIntf` 的值和类型, 所有 `case` 语句中列举的类型(`nil` 除外)都必须实现对应的接口(在上例中即 `Shaper`),如果被检测类型没有在 `case` 语句列举的类型中,就会执行 `default` 语句。
-
-可以用 `type-switch` 进行运行时类型分析,但是在 `type-switch` 不允许有 `fallthrough` 。
-
-如果仅仅是测试变量的类型,不用它的值,那么就可以不需要赋值语句,比如:
-
-```go
-switch areaIntf.(type) {
-case *Square:
-	// TODO
-case *Circle:
-	// TODO
-...
-default:
-	// TODO
-}
-```
-
-下面的代码片段展示了一个类型分类函数,它有一个可变长度参数,可以是任意类型的数组,它会根据数组元素的实际类型执行不同的动作:
-
-```go
-func classifier(items ...interface{}) {
-	for i, x := range items {
-		switch x.(type) {
-		case bool:
-			fmt.Printf("Param #%d is a bool\n", i)
-		case float64:
-			fmt.Printf("Param #%d is a float64\n", i)
-		case int, int64:
-			fmt.Printf("Param #%d is a int\n", i)
-		case nil:
-			fmt.Printf("Param #%d is a nil\n", i)
-		case string:
-			fmt.Printf("Param #%d is a string\n", i)
-		default:
-			fmt.Printf("Param #%d is unknown\n", i)
-		}
-	}
-}
-```
-
-可以这样调用此方法:`classifier(13, -14.3, "BELGIUM", complex(1, 2), nil, false)` 。
-
-在处理来自于外部的、类型未知的数据时,比如解析诸如 JSON 或 XML 编码的数据,类型测试和转换会非常有用。
-
-在示例 12.17(xml.go)中解析 XML 文档时,我们就会用到 `type-switch`。
-
-**练习 11.4** simple_interface2.go:
-
-接着练习 11.1 中的内容,创建第二个类型 `RSimple`,它也实现了接口 `Simpler`,写一个函数 `fi`,使它可以区分 `Simple` 和 `RSimple` 类型的变量。
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[类型断言:如何检测和转换接口变量的类型](11.3.md)
-- 下一节:[测试一个值是否实现了某个接口](11.5.md)
+# 11.4 类型判断:type-switch
+
+接口变量的类型也可以使用一种特殊形式的 `switch` 来检测:**type-switch** (下面是示例 11.4 的第二部分):
+
+```go
+switch t := areaIntf.(type) {
+case *Square:
+	fmt.Printf("Type Square %T with value %v\n", t, t)
+case *Circle:
+	fmt.Printf("Type Circle %T with value %v\n", t, t)
+case nil:
+	fmt.Printf("nil value: nothing to check?\n")
+default:
+	fmt.Printf("Unexpected type %T\n", t)
+}
+```
+
+输出:
+
+    Type Square *main.Square with value &{5}
+
+变量 `t` 得到了 `areaIntf` 的值和类型, 所有 `case` 语句中列举的类型(`nil` 除外)都必须实现对应的接口(在上例中即 `Shaper`),如果被检测类型没有在 `case` 语句列举的类型中,就会执行 `default` 语句。
+
+可以用 `type-switch` 进行运行时类型分析,但是在 `type-switch` 不允许有 `fallthrough` 。
+
+如果仅仅是测试变量的类型,不用它的值,那么就可以不需要赋值语句,比如:
+
+```go
+switch areaIntf.(type) {
+case *Square:
+	// TODO
+case *Circle:
+	// TODO
+...
+default:
+	// TODO
+}
+```
+
+下面的代码片段展示了一个类型分类函数,它有一个可变长度参数,可以是任意类型的数组,它会根据数组元素的实际类型执行不同的动作:
+
+```go
+func classifier(items ...interface{}) {
+	for i, x := range items {
+		switch x.(type) {
+		case bool:
+			fmt.Printf("Param #%d is a bool\n", i)
+		case float64:
+			fmt.Printf("Param #%d is a float64\n", i)
+		case int, int64:
+			fmt.Printf("Param #%d is a int\n", i)
+		case nil:
+			fmt.Printf("Param #%d is a nil\n", i)
+		case string:
+			fmt.Printf("Param #%d is a string\n", i)
+		default:
+			fmt.Printf("Param #%d is unknown\n", i)
+		}
+	}
+}
+```
+
+可以这样调用此方法:`classifier(13, -14.3, "BELGIUM", complex(1, 2), nil, false)` 。
+
+在处理来自于外部的、类型未知的数据时,比如解析诸如 JSON 或 XML 编码的数据,类型测试和转换会非常有用。
+
+在示例 12.17(xml.go)中解析 XML 文档时,我们就会用到 `type-switch`。
+
+**练习 11.4** simple_interface2.go:
+
+接着练习 11.1 中的内容,创建第二个类型 `RSimple`,它也实现了接口 `Simpler`,写一个函数 `fi`,使它可以区分 `Simple` 和 `RSimple` 类型的变量。
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[类型断言:如何检测和转换接口变量的类型](11.3.md)
+- 下一节:[测试一个值是否实现了某个接口](11.5.md)

+ 31 - 31
eBook/11.5.md

@@ -1,31 +1,31 @@
-# 11.5 测试一个值是否实现了某个接口
-
-这是 11.3 类型断言中的一个特例:假定 `v` 是一个值,然后我们想测试它是否实现了 `Stringer` 接口,可以这样做:
-
-```go
-type Stringer interface {
-    String() string
-}
-
-if sv, ok := v.(Stringer); ok {
-    fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v
-}
-```
-
-`Print` 函数就是如此检测类型是否可以打印自身的。
-
-接口是一种契约,实现类型必须满足它,它描述了类型的行为,规定类型可以做什么。接口彻底将类型能做什么,以及如何做分离开来,使得相同接口的变量在不同的时刻表现出不同的行为,这就是多态的本质。
-
-编写参数是接口变量的函数,这使得它们更具有一般性。
-
-**使用接口使代码更具有普适性。**
-
-标准库里到处都使用了这个原则,如果对接口概念没有良好的把握,是不可能理解它是如何构建的。
-
-在接下来的章节中,我们会讨论两个重要的例子,试着去深入理解它们,这样你就可以更好的应用上面的原则。
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[类型判断:type-switch](11.4.md)
-- 下一节:[使用方法集与接口](11.6.md)
+# 11.5 测试一个值是否实现了某个接口
+
+这是 11.3 类型断言中的一个特例:假定 `v` 是一个值,然后我们想测试它是否实现了 `Stringer` 接口,可以这样做:
+
+```go
+type Stringer interface {
+    String() string
+}
+
+if sv, ok := v.(Stringer); ok {
+    fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v
+}
+```
+
+`Print` 函数就是如此检测类型是否可以打印自身的。
+
+接口是一种契约,实现类型必须满足它,它描述了类型的行为,规定类型可以做什么。接口彻底将类型能做什么,以及如何做分离开来,使得相同接口的变量在不同的时刻表现出不同的行为,这就是多态的本质。
+
+编写参数是接口变量的函数,这使得它们更具有一般性。
+
+**使用接口使代码更具有普适性。**
+
+标准库里到处都使用了这个原则,如果对接口概念没有良好的把握,是不可能理解它是如何构建的。
+
+在接下来的章节中,我们会讨论两个重要的例子,试着去深入理解它们,这样你就可以更好的应用上面的原则。
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[类型判断:type-switch](11.4.md)
+- 下一节:[使用方法集与接口](11.6.md)

+ 92 - 92
eBook/11.6.md

@@ -1,92 +1,92 @@
-# 11.6 使用方法集与接口
-
-在第 10.6.3 节及例子 methodset1.go 中我们看到,作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,幸运的是,如果使用不当编译器会给出错误。考虑下面的程序:
-
-示例 11.5 [methodset2.go](examples/chapter_11/methodset2.go):
-
-```go
-package main
-
-import (
-	"fmt"
-)
-
-type List []int
-
-func (l List) Len() int {
-	return len(l)
-}
-
-func (l *List) Append(val int) {
-	*l = append(*l, val)
-}
-
-type Appender interface {
-	Append(int)
-}
-
-func CountInto(a Appender, start, end int) {
-	for i := start; i <= end; i++ {
-		a.Append(i)
-	}
-}
-
-type Lener interface {
-	Len() int
-}
-
-func LongEnough(l Lener) bool {
-	return l.Len()*10 > 42
-}
-
-func main() {
-	// A bare value
-	var lst List
-	// compiler error:
-	// cannot use lst (type List) as type Appender in argument to CountInto:
-	//       List does not implement Appender (Append method has pointer receiver)
-	// CountInto(lst, 1, 10)
-	if LongEnough(lst) { // VALID:Identical receiver type
-		fmt.Printf("- lst is long enough\n")
-	}
-
-	// A pointer value
-	plst := new(List)
-	CountInto(plst, 1, 10) //VALID:Identical receiver type
-	if LongEnough(plst) {
-		// VALID: a *List can be dereferenced for the receiver
-		fmt.Printf("- plst is long enough\n")
-	}
-}
-```
-
-**讨论**
-
-在 `lst` 上调用 `CountInto` 时会导致一个编译器错误,因为 `CountInto` 需要一个 `Appender`,而它的方法 `Append` 只定义在指针上。 在 `lst` 上调用 `LongEnough` 是可以的,因为 `Len` 定义在值上。
-
-在 `plst` 上调用 `CountInto` 是可以的,因为 `CountInto` 需要一个 `Appender`,并且它的方法 `Append` 定义在指针上。 在 `plst` 上调用 `LongEnough` 也是可以的,因为指针会被自动解引用。
-
-**总结**
-
-在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以从具体类型 `P` 直接可以辨识的:
-
-- 指针方法可以通过指针调用
-- 值方法可以通过值调用
-- 接收者是值的方法可以通过指针调用,因为指针会首先被解引用
-- 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址
-
-将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。
-
-**译注**
-
-Go 语言规范定义了接口方法集的调用规则:
-
-- 类型 T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
-- 类型 *T 的可调用方法集包含接受者为 *T 的所有方法
-- 类型 *T 的可调用方法集不包含接受者为 T 的方法
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[测试一个值是否实现了某个接口](11.5.md)
-- 下一节:[第一个例子:使用 Sorter 接口排序](11.7.md)
+# 11.6 使用方法集与接口
+
+在第 10.6.3 节及例子 methodset1.go 中我们看到,作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,幸运的是,如果使用不当编译器会给出错误。考虑下面的程序:
+
+示例 11.5 [methodset2.go](examples/chapter_11/methodset2.go):
+
+```go
+package main
+
+import (
+	"fmt"
+)
+
+type List []int
+
+func (l List) Len() int {
+	return len(l)
+}
+
+func (l *List) Append(val int) {
+	*l = append(*l, val)
+}
+
+type Appender interface {
+	Append(int)
+}
+
+func CountInto(a Appender, start, end int) {
+	for i := start; i <= end; i++ {
+		a.Append(i)
+	}
+}
+
+type Lener interface {
+	Len() int
+}
+
+func LongEnough(l Lener) bool {
+	return l.Len()*10 > 42
+}
+
+func main() {
+	// A bare value
+	var lst List
+	// compiler error:
+	// cannot use lst (type List) as type Appender in argument to CountInto:
+	//       List does not implement Appender (Append method has pointer receiver)
+	// CountInto(lst, 1, 10)
+	if LongEnough(lst) { // VALID:Identical receiver type
+		fmt.Printf("- lst is long enough\n")
+	}
+
+	// A pointer value
+	plst := new(List)
+	CountInto(plst, 1, 10) //VALID:Identical receiver type
+	if LongEnough(plst) {
+		// VALID: a *List can be dereferenced for the receiver
+		fmt.Printf("- plst is long enough\n")
+	}
+}
+```
+
+**讨论**
+
+在 `lst` 上调用 `CountInto` 时会导致一个编译器错误,因为 `CountInto` 需要一个 `Appender`,而它的方法 `Append` 只定义在指针上。 在 `lst` 上调用 `LongEnough` 是可以的,因为 `Len` 定义在值上。
+
+在 `plst` 上调用 `CountInto` 是可以的,因为 `CountInto` 需要一个 `Appender`,并且它的方法 `Append` 定义在指针上。 在 `plst` 上调用 `LongEnough` 也是可以的,因为指针会被自动解引用。
+
+**总结**
+
+在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以从具体类型 `P` 直接可以辨识的:
+
+- 指针方法可以通过指针调用
+- 值方法可以通过值调用
+- 接收者是值的方法可以通过指针调用,因为指针会首先被解引用
+- 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址
+
+将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。
+
+**译注**
+
+Go 语言规范定义了接口方法集的调用规则:
+
+- 类型 T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
+- 类型 *T 的可调用方法集包含接受者为 *T 的所有方法
+- 类型 *T 的可调用方法集不包含接受者为 T 的方法
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[测试一个值是否实现了某个接口](11.5.md)
+- 下一节:[第一个例子:使用 Sorter 接口排序](11.7.md)

+ 227 - 227
eBook/11.7.md

@@ -1,227 +1,227 @@
-# 11.7 第一个例子:使用 Sorter 接口排序
-
-一个很好的例子是来自标准库的 `sort` 包,要对一组数字或字符串排序,只需要实现三个方法:反映元素个数的 `Len()`方法、比较第 `i` 和 `j` 个元素的 `Less(i, j)` 方法以及交换第 `i` 和 `j` 个元素的 `Swap(i, j)` 方法。
-
-排序函数的算法只会使用到这三个方法(可以使用任何排序算法来实现,此处我们使用冒泡排序):
-
-```go
-func Sort(data Sorter) {
-    for pass := 1; pass < data.Len(); pass++ {
-        for i := 0;i < data.Len() - pass; i++ {
-            if data.Less(i+1, i) {
-                data.Swap(i, i + 1)
-            }
-        }
-    }
-}
-```
-
-`Sort` 函数接收一个接口类型的参数:`Sorter` ,它声明了这些方法:
-
-```go
-type Sorter interface {
-    Len() int
-    Less(i, j int) bool
-    Swap(i, j int)
-}
-```
-
-参数中的 `int` 是待排序序列长度的类型,而不是说要排序的对象一定要是一组 `int`。`i` 和 `j` 表示元素的整型索引,长度也是整型的。
-
-现在如果我们想对一个 `int` 数组进行排序,所有必须做的事情就是:为数组定一个类型并在它上面实现 `Sorter` 接口的方法:
-
-```go
-type IntArray []int
-func (p IntArray) Len() int           { return len(p) }
-func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
-func (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
-```
-
-下面是调用排序函数的一个具体例子:
-
-```go
-data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
-a := sort.IntArray(data) //conversion to type IntArray from package sort
-sort.Sort(a)
-```
-
-完整的、可运行的代码可以在 `sort.go` 和 `sortmain.go` 里找到。
-
-同样的原理,排序函数可以用于一个浮点型数组,一个字符串数组,或者一个表示每周各天的结构体 `dayArray`。
-
-示例 11.6 [sort.go](examples/chapter_11/sort/sort.go):
-
-```go
-package sort
-
-type Sorter interface {
-	Len() int
-	Less(i, j int) bool
-	Swap(i, j int)
-}
-
-func Sort(data Sorter) {
-	for pass := 1; pass < data.Len(); pass++ {
-		for i := 0; i < data.Len()-pass; i++ {
-			if data.Less(i+1, i) {
-				data.Swap(i, i+1)
-			}
-		}
-	}
-}
-
-func IsSorted(data Sorter) bool {
-	n := data.Len()
-	for i := n - 1; i > 0; i-- {
-		if data.Less(i, i-1) {
-			return false
-		}
-	}
-	return true
-}
-
-// Convenience types for common cases
-type IntArray []int
-
-func (p IntArray) Len() int           { return len(p) }
-func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
-func (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
-
-type StringArray []string
-
-func (p StringArray) Len() int           { return len(p) }
-func (p StringArray) Less(i, j int) bool { return p[i] < p[j] }
-func (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
-
-// Convenience wrappers for common cases
-func SortInts(a []int)       { Sort(IntArray(a)) }
-func SortStrings(a []string) { Sort(StringArray(a)) }
-
-func IntsAreSorted(a []int) bool       { return IsSorted(IntArray(a)) }
-func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }
-```
-
-示例 11.7 [sortmain.go](examples/chapter_11/sortmain.go):
-
-```go
-package main
-
-import (
-	"./sort"
-	"fmt"
-)
-
-func ints() {
-	data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
-	a := sort.IntArray(data) //conversion to type IntArray
-	sort.Sort(a)
-	if !sort.IsSorted(a) {
-		panic("fails")
-	}
-	fmt.Printf("The sorted array is: %v\n", a)
-}
-
-func strings() {
-	data := []string{"monday", "friday", "tuesday", "wednesday", "sunday", "thursday", "", "saturday"}
-	a := sort.StringArray(data)
-	sort.Sort(a)
-	if !sort.IsSorted(a) {
-		panic("fail")
-	}
-	fmt.Printf("The sorted array is: %v\n", a)
-}
-
-type day struct {
-	num       int
-	shortName string
-	longName  string
-}
-
-type dayArray struct {
-	data []*day
-}
-
-func (p *dayArray) Len() int           { return len(p.data) }
-func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }
-func (p *dayArray) Swap(i, j int)      { p.data[i], p.data[j] = p.data[j], p.data[i] }
-
-func days() {
-	Sunday    := day{0, "SUN", "Sunday"}
-	Monday    := day{1, "MON", "Monday"}
-	Tuesday   := day{2, "TUE", "Tuesday"}
-	Wednesday := day{3, "WED", "Wednesday"}
-	Thursday  := day{4, "THU", "Thursday"}
-	Friday    := day{5, "FRI", "Friday"}
-	Saturday  := day{6, "SAT", "Saturday"}
-	data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
-	a := dayArray{data}
-	sort.Sort(&a)
-	if !sort.IsSorted(&a) {
-		panic("fail")
-	}
-	for _, d := range data {
-		fmt.Printf("%s ", d.longName)
-	}
-	fmt.Printf("\n")
-}
-
-func main() {
-	ints()
-	strings()
-	days()
-}
-```
-
-输出:
-
-    The sorted array is: [-5467984 -784 0 0 42 59 74 238 905 959 7586 7586 9845]
-    The sorted array is: [ friday monday saturday sunday thursday tuesday wednesday]
-    Sunday Monday Tuesday Wednesday Thursday Friday Saturday 
-
-**备注**:
-
-`panic("fail")` 用于停止处于在非正常情况下的程序(详细请参考 第13章),当然也可以先打印一条信息,然后调用 `os.Exit(1)` 来停止程序。
-
-上面的例子帮助我们进一步了解了接口的意义和使用方式。对于基本类型的排序,标准库已经提供了相关的排序函数,所以不需要我们再重复造轮子了。对于一般性的排序,`sort` 包定义了一个接口:
-
-```go
-type Interface interface {
-	Len() int
-	Less(i, j int) bool
-	Swap(i, j int)
-}
-```
-
-这个接口总结了需要用于排序的抽象方法,函数 `Sort(data Interface)` 用来对此类对象进行排序,可以用它们来实现对其他类型的数据(非基本类型)进行排序。在上面的例子中,我们也是这么做的,不仅可以对 `int` 和 `string` 序列进行排序,也可以对用户自定义类型 `dayArray` 进行排序。
-
-**练习 11.5** interfaces_ext.go:
-
-a). 继续扩展程序,定义类型 `Triangle`,让它实现 `AreaInterface` 接口。通过计算一个特定三角形的面积来进行测试(三角形面积=0.5 * (底 * 高))
-
-b). 定义一个新接口 `PeriInterface`,它有一个 `Perimeter` 方法。让 `Square` 实现这个接口,并通过一个 `Square` 示例来测试它。
-
-**练习 11.6** point_interfaces.go:
-
-继续 10.3 中的练习 point_methods.go,定义接口 `Magnitude`,它有一个方法 `Abs()`。让 `Point`、`Point3` 及`Polar` 实现此接口。通过接口类型变量使用方法做point.go中同样的事情。
-
-**练习 11.7** float_sort.go / float_sortmain.go:
-
-类似11.7和示例11.3/4,定义一个包 `float64`,并在包里定义类型 `Float64Array`,然后让它实现 `Sorter` 接口用来对 `float64` 数组进行排序。
-
-另外提供如下方法:
-
-- `NewFloat64Array()`:创建一个包含25个元素的数组变量(参考10.2)
-- `List()`:返回数组格式化后的字符串,并在 `String()` 方法中调用它,这样就不用显式地调用 `List()` 来打印数组(参考10.7)
-- `Fill()`:创建一个包含10个随机浮点数的数组(参考4.5.2.6)
-
-在主程序中新建一个此类型的变量,然后对它排序并进行测试。
-
-**练习 11.8** sort.go/sort_persons.go:
-
-定义一个结构体 `Person`,它有两个字段:`firstName` 和 `lastName`,为 `[]Person` 定义类型 `Persons` 。让 `Persons` 实现 `Sorter` 接口并进行测试。
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[使用方法集与接口](11.6.md)
-- 下一节:[第二个例子:读和写](11.8.md)
+# 11.7 第一个例子:使用 Sorter 接口排序
+
+一个很好的例子是来自标准库的 `sort` 包,要对一组数字或字符串排序,只需要实现三个方法:反映元素个数的 `Len()`方法、比较第 `i` 和 `j` 个元素的 `Less(i, j)` 方法以及交换第 `i` 和 `j` 个元素的 `Swap(i, j)` 方法。
+
+排序函数的算法只会使用到这三个方法(可以使用任何排序算法来实现,此处我们使用冒泡排序):
+
+```go
+func Sort(data Sorter) {
+    for pass := 1; pass < data.Len(); pass++ {
+        for i := 0;i < data.Len() - pass; i++ {
+            if data.Less(i+1, i) {
+                data.Swap(i, i + 1)
+            }
+        }
+    }
+}
+```
+
+`Sort` 函数接收一个接口类型的参数:`Sorter` ,它声明了这些方法:
+
+```go
+type Sorter interface {
+    Len() int
+    Less(i, j int) bool
+    Swap(i, j int)
+}
+```
+
+参数中的 `int` 是待排序序列长度的类型,而不是说要排序的对象一定要是一组 `int`。`i` 和 `j` 表示元素的整型索引,长度也是整型的。
+
+现在如果我们想对一个 `int` 数组进行排序,所有必须做的事情就是:为数组定一个类型并在它上面实现 `Sorter` 接口的方法:
+
+```go
+type IntArray []int
+func (p IntArray) Len() int           { return len(p) }
+func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
+func (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+```
+
+下面是调用排序函数的一个具体例子:
+
+```go
+data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
+a := sort.IntArray(data) //conversion to type IntArray from package sort
+sort.Sort(a)
+```
+
+完整的、可运行的代码可以在 `sort.go` 和 `sortmain.go` 里找到。
+
+同样的原理,排序函数可以用于一个浮点型数组,一个字符串数组,或者一个表示每周各天的结构体 `dayArray`。
+
+示例 11.6 [sort.go](examples/chapter_11/sort/sort.go):
+
+```go
+package sort
+
+type Sorter interface {
+	Len() int
+	Less(i, j int) bool
+	Swap(i, j int)
+}
+
+func Sort(data Sorter) {
+	for pass := 1; pass < data.Len(); pass++ {
+		for i := 0; i < data.Len()-pass; i++ {
+			if data.Less(i+1, i) {
+				data.Swap(i, i+1)
+			}
+		}
+	}
+}
+
+func IsSorted(data Sorter) bool {
+	n := data.Len()
+	for i := n - 1; i > 0; i-- {
+		if data.Less(i, i-1) {
+			return false
+		}
+	}
+	return true
+}
+
+// Convenience types for common cases
+type IntArray []int
+
+func (p IntArray) Len() int           { return len(p) }
+func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
+func (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+type StringArray []string
+
+func (p StringArray) Len() int           { return len(p) }
+func (p StringArray) Less(i, j int) bool { return p[i] < p[j] }
+func (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+// Convenience wrappers for common cases
+func SortInts(a []int)       { Sort(IntArray(a)) }
+func SortStrings(a []string) { Sort(StringArray(a)) }
+
+func IntsAreSorted(a []int) bool       { return IsSorted(IntArray(a)) }
+func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }
+```
+
+示例 11.7 [sortmain.go](examples/chapter_11/sortmain.go):
+
+```go
+package main
+
+import (
+	"./sort"
+	"fmt"
+)
+
+func ints() {
+	data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
+	a := sort.IntArray(data) //conversion to type IntArray
+	sort.Sort(a)
+	if !sort.IsSorted(a) {
+		panic("fails")
+	}
+	fmt.Printf("The sorted array is: %v\n", a)
+}
+
+func strings() {
+	data := []string{"monday", "friday", "tuesday", "wednesday", "sunday", "thursday", "", "saturday"}
+	a := sort.StringArray(data)
+	sort.Sort(a)
+	if !sort.IsSorted(a) {
+		panic("fail")
+	}
+	fmt.Printf("The sorted array is: %v\n", a)
+}
+
+type day struct {
+	num       int
+	shortName string
+	longName  string
+}
+
+type dayArray struct {
+	data []*day
+}
+
+func (p *dayArray) Len() int           { return len(p.data) }
+func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }
+func (p *dayArray) Swap(i, j int)      { p.data[i], p.data[j] = p.data[j], p.data[i] }
+
+func days() {
+	Sunday    := day{0, "SUN", "Sunday"}
+	Monday    := day{1, "MON", "Monday"}
+	Tuesday   := day{2, "TUE", "Tuesday"}
+	Wednesday := day{3, "WED", "Wednesday"}
+	Thursday  := day{4, "THU", "Thursday"}
+	Friday    := day{5, "FRI", "Friday"}
+	Saturday  := day{6, "SAT", "Saturday"}
+	data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
+	a := dayArray{data}
+	sort.Sort(&a)
+	if !sort.IsSorted(&a) {
+		panic("fail")
+	}
+	for _, d := range data {
+		fmt.Printf("%s ", d.longName)
+	}
+	fmt.Printf("\n")
+}
+
+func main() {
+	ints()
+	strings()
+	days()
+}
+```
+
+输出:
+
+    The sorted array is: [-5467984 -784 0 0 42 59 74 238 905 959 7586 7586 9845]
+    The sorted array is: [ friday monday saturday sunday thursday tuesday wednesday]
+    Sunday Monday Tuesday Wednesday Thursday Friday Saturday 
+
+**备注**:
+
+`panic("fail")` 用于停止处于在非正常情况下的程序(详细请参考 第13章),当然也可以先打印一条信息,然后调用 `os.Exit(1)` 来停止程序。
+
+上面的例子帮助我们进一步了解了接口的意义和使用方式。对于基本类型的排序,标准库已经提供了相关的排序函数,所以不需要我们再重复造轮子了。对于一般性的排序,`sort` 包定义了一个接口:
+
+```go
+type Interface interface {
+	Len() int
+	Less(i, j int) bool
+	Swap(i, j int)
+}
+```
+
+这个接口总结了需要用于排序的抽象方法,函数 `Sort(data Interface)` 用来对此类对象进行排序,可以用它们来实现对其他类型的数据(非基本类型)进行排序。在上面的例子中,我们也是这么做的,不仅可以对 `int` 和 `string` 序列进行排序,也可以对用户自定义类型 `dayArray` 进行排序。
+
+**练习 11.5** interfaces_ext.go:
+
+a). 继续扩展程序,定义类型 `Triangle`,让它实现 `AreaInterface` 接口。通过计算一个特定三角形的面积来进行测试(三角形面积=0.5 * (底 * 高))
+
+b). 定义一个新接口 `PeriInterface`,它有一个 `Perimeter` 方法。让 `Square` 实现这个接口,并通过一个 `Square` 示例来测试它。
+
+**练习 11.6** point_interfaces.go:
+
+继续 10.3 中的练习 point_methods.go,定义接口 `Magnitude`,它有一个方法 `Abs()`。让 `Point`、`Point3` 及`Polar` 实现此接口。通过接口类型变量使用方法做point.go中同样的事情。
+
+**练习 11.7** float_sort.go / float_sortmain.go:
+
+类似11.7和示例11.3/4,定义一个包 `float64`,并在包里定义类型 `Float64Array`,然后让它实现 `Sorter` 接口用来对 `float64` 数组进行排序。
+
+另外提供如下方法:
+
+- `NewFloat64Array()`:创建一个包含25个元素的数组变量(参考10.2)
+- `List()`:返回数组格式化后的字符串,并在 `String()` 方法中调用它,这样就不用显式地调用 `List()` 来打印数组(参考10.7)
+- `Fill()`:创建一个包含10个随机浮点数的数组(参考4.5.2.6)
+
+在主程序中新建一个此类型的变量,然后对它排序并进行测试。
+
+**练习 11.8** sort.go/sort_persons.go:
+
+定义一个结构体 `Person`,它有两个字段:`firstName` 和 `lastName`,为 `[]Person` 定义类型 `Persons` 。让 `Persons` 实现 `Sorter` 接口并进行测试。
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[使用方法集与接口](11.6.md)
+- 下一节:[第二个例子:读和写](11.8.md)

+ 29 - 29
eBook/11.8.md

@@ -1,29 +1,29 @@
-# 11.8 第二个例子:读和写
-
-读和写是软件中很普遍的行为,提起它们会立即想到读写文件、缓存(比如字节或字符串切片)、标准输入输出、标准错误以及网络连接、管道等等,或者读写我们的自定义类型。为了让代码尽可能通用,Go 采取了一致的方式来读写数据。
-
-`io` 包提供了用于读和写的接口 `io.Reader` 和 `io.Writer`:
-
-```go
-type Reader interface {
-    Read(p []byte) (n int, err error)
-}
-
-type Writer interface {
-    Write(p []byte) (n int, err error)
-}
-```
-
-只要类型实现了读写接口,提供 `Read()` 和 `Write` 方法,就可以从它读取数据,或向它写入数据。一个对象要是可读的,它必须实现 `io.Reader` 接口,这个接口只有一个签名是 `Read(p []byte) (n int, err error)` 的方法,它从调用它的对象上读取数据,并把读到的数据放入参数中的字节切片中,然后返回读取的字节数和一个 `error` 对象,如果没有错误发生返回 `nil`,如果已经到达输入的尾端,会返回 `io.EOF("EOF")`,如果读取的过程中发生了错误,就会返回具体的错误信息。类似地,一个对象要是可写的,它必须实现 `io.Writer` 接口,这个接口也只有一个签名是 `Write(p []byte) (n int, err error)` 的方法,它将指定字节切片中的数据写入调用它的对象里,然后返回实际写入的字节数和一个 `error` 对象(如果没有错误发生就是 `nil`)。
-
-`io` 包里的 `Readers` 和 `Writers` 都是不带缓冲的,`bufio` 包里提供了对应的带缓冲的操作,在读写 `UTF-8` 编码的文本文件时它们尤其有用。在 第12章 我们会看到很多在实战中使用它们的例子。
-
-在实际编程中尽可能的使用这些接口,会使程序变得更通用,可以在任何实现了这些接口的类型上使用读写方法。
-
-例如一个 `JPEG` 图形解码器,通过一个 `Reader` 参数,它可以解码来自磁盘、网络连接或以 `gzip` 压缩的 `HTTP` 流中的 `JPEG` 图形数据,或者其他任何实现了 `Reader` 接口的对象。 
-
-## 链接
-
-- [目录](directory.md)
-- 上一节:[第一个例子:使用Sorter接口排序](11.7.md)
-- 下一节:[空接口](11.9.md)
+# 11.8 第二个例子:读和写
+
+读和写是软件中很普遍的行为,提起它们会立即想到读写文件、缓存(比如字节或字符串切片)、标准输入输出、标准错误以及网络连接、管道等等,或者读写我们的自定义类型。为了让代码尽可能通用,Go 采取了一致的方式来读写数据。
+
+`io` 包提供了用于读和写的接口 `io.Reader` 和 `io.Writer`:
+
+```go
+type Reader interface {
+    Read(p []byte) (n int, err error)
+}
+
+type Writer interface {
+    Write(p []byte) (n int, err error)
+}
+```
+
+只要类型实现了读写接口,提供 `Read()` 和 `Write` 方法,就可以从它读取数据,或向它写入数据。一个对象要是可读的,它必须实现 `io.Reader` 接口,这个接口只有一个签名是 `Read(p []byte) (n int, err error)` 的方法,它从调用它的对象上读取数据,并把读到的数据放入参数中的字节切片中,然后返回读取的字节数和一个 `error` 对象,如果没有错误发生返回 `nil`,如果已经到达输入的尾端,会返回 `io.EOF("EOF")`,如果读取的过程中发生了错误,就会返回具体的错误信息。类似地,一个对象要是可写的,它必须实现 `io.Writer` 接口,这个接口也只有一个签名是 `Write(p []byte) (n int, err error)` 的方法,它将指定字节切片中的数据写入调用它的对象里,然后返回实际写入的字节数和一个 `error` 对象(如果没有错误发生就是 `nil`)。
+
+`io` 包里的 `Readers` 和 `Writers` 都是不带缓冲的,`bufio` 包里提供了对应的带缓冲的操作,在读写 `UTF-8` 编码的文本文件时它们尤其有用。在 第12章 我们会看到很多在实战中使用它们的例子。
+
+在实际编程中尽可能的使用这些接口,会使程序变得更通用,可以在任何实现了这些接口的类型上使用读写方法。
+
+例如一个 `JPEG` 图形解码器,通过一个 `Reader` 参数,它可以解码来自磁盘、网络连接或以 `gzip` 压缩的 `HTTP` 流中的 `JPEG` 图形数据,或者其他任何实现了 `Reader` 接口的对象。 
+
+## 链接
+
+- [目录](directory.md)
+- 上一节:[第一个例子:使用Sorter接口排序](11.7.md)
+- 下一节:[空接口](11.9.md)

+ 1 - 1
eBook/12.0.md

@@ -1,4 +1,4 @@
-# 12 读写数据
+# 12.0 读写数据
 
 除了 fmt 和 os 包,我们还需要用到 bufio 包来处理缓冲的输入和输出。
 

+ 1 - 1
eBook/13.0.md

@@ -1,4 +1,4 @@
-# 13 错误处理与测试
+# 13.0 错误处理与测试
 
 Go 没有像 Java 和 .NET 那样的 `try/catch` 异常机制:不能执行抛异常操作。但是有一套 `defer-panic-and-recover` 机制(参见 13.2-13.3 节)。
 

+ 1 - 1
eBook/14.0.md

@@ -1,4 +1,4 @@
-# 14 协程(goroutine)与通道(channel)
+# 14.0 协程(goroutine)与通道(channel)
 
 作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算,参见第 15 章)和程序的并发。程序可以在不同的处理器和计算机上同时执行不同的代码段。Go 语言为构建并发程序的基本代码块是 协程 (goroutine) 与通道 (channel)。他们需要语言,编译器,和runtime的支持。Go 语言提供的垃圾回收器对并发编程至关重要。
 

+ 1 - 1
eBook/15.0.md

@@ -1,4 +1,4 @@
-# 15.0 网络,模板和网页应用
+# 15.0 网络、模板与网页应用
 
 Go 在编写 web 应用方面非常得力。因为目前它还没有GUI(Graphic User Interface 即图形化用户界面)的框架,通过文本或者模板展现的 html 页面是目前 Go 编写界面应用程序的唯一方式。(**译者注:实际上在翻译的时候,已经有了一些不太成熟的GUI库例如:go ui。**)
 

+ 1 - 1
eBook/15.2.md

@@ -1,4 +1,4 @@
-# 15.2 一个简单的网页服务器
+# 15.2 一个简单的 web 服务器
 
 http 是比 tcp 更高层的协议,它描述了网页服务器如何与客户端浏览器进行通信。Go 提供了 `net/http` 包,我们马上就来看下。先从一些简单的示例开始,首先编写一个“Hello world!”网页服务器:[查看示例15.6](examples/chapter_15/hello_world_webserver.go)
 

+ 1 - 1
eBook/15.3.md

@@ -1,4 +1,4 @@
-# 15.3 访问并读取页面
+# 15.3 访问并读取页面数据
 
 在下边这个程序中,数组中的 url 都将被访问:会发送一个简单的 `http.Head()` 请求查看返回值;它的声明如下:`func Head(url string) (r *Response, err error)`
 

+ 1 - 1
eBook/16.0.md

@@ -1,4 +1,4 @@
-# 16 常见的陷阱与错误
+# 16.0 常见的陷阱与错误
 
 在之前的内容中,有时候使用`!!...!!`标记警告go语言中的一些错误使用方式。当你在编程时候遇到的一个困难,可以确定本书特定的章节能找到类似的主题。为了方便起见,这里列出了一些常见陷阱,以便于你能发现更多的解释和例子:
 

+ 1 - 1
eBook/16.1.md

@@ -38,4 +38,4 @@ func shadow() (err error) {
 
 - [目录](directory.md)
 - 上一节:[常见的陷阱与错误](16.0.md)
-- 下一节:[误用字符串](16.2.md)
+- 下一节:[误用字符串](16.2.md)

+ 2 - 2
eBook/16.3.md

@@ -1,4 +1,4 @@
-# 16.3 发生错误时使用defer关闭一个文件
+# 16.3 发生错误时使用 defer 关闭一个文件
 
 如果你在一个for循环内部处理一系列文件,你需要使用defer确保文件在处理完毕后被关闭,例如:
 
@@ -34,4 +34,4 @@ for _, file := range files {
 
 - [目录](directory.md)
 - 上一节:[误用字符串](16.2.md)
-- 下一节:[何时使用new()和make()](16.4.md)
+- 下一节:[何时使用new()和make()](16.4.md)

+ 2 - 2
eBook/16.4.md

@@ -1,4 +1,4 @@
-# 16.4 何时使用new()和make()
+# 16.4 何时使用 new()  make()
 
 在第[7.2.1小节](07.2.md)和第[10.2.2](10.2.md)小节,我们已经讨论过此问题,并使用代码进行详细说明,观点如下:
 
@@ -9,4 +9,4 @@
 
 - [目录](directory.md)
 - 上一节:[发生错误时使用defer关闭一个文件](16.3.md)
-- 下一节:[不需要将一个指向切片的指针传递给函数](16.5.md)
+- 下一节:[不需要将一个指向切片的指针传递给函数](16.5.md)

+ 1 - 1
eBook/16.7.md

@@ -6,4 +6,4 @@
 
 - [目录](directory.md)
 - 上一节:[使用指针指向接口类型](16.6.md)
-- 下一节:[误用协程和通道](16.8.md)
+- 下一节:[误用协程和通道](16.8.md)

+ 1 - 1
eBook/16.8.md

@@ -8,4 +8,4 @@
 
 - [目录](directory.md)
 - 上一节:[使用值类型时误用指针](16.7.md)
-- 下一节:[闭包和协程的使用](16.9.md)
+- 下一节:[闭包和协程的使用](16.9.md)

+ 1 - 1
eBook/16.9.md

@@ -72,4 +72,4 @@ func main() {
 
 - [目录](directory.md)
 - 上一节:[误用协程和通道](16.8.md)
-- 下一节:[糟糕的错误处理](16.10.md)
+- 下一节:[糟糕的错误处理](16.10.md)

+ 1 - 1
eBook/17.0.md

@@ -1,4 +1,4 @@
-# 17 Go 语言模式
+# 17.0 模式
 
 ## 链接
 

+ 2 - 2
eBook/18.0.md

@@ -1,7 +1,7 @@
-# 18 出于性能考虑的实用代码片段
+# 18.0 出于性能考虑的实用代码片段
 
 ## 链接
 
 - [目录](directory.md)
 - 上一章:[运算符模式和接口](17.4.md)
-- 下一节:[字符串](18.1.md)
+- 下一节:[字符串](18.1.md)

+ 1 - 1
eBook/18.10.md

@@ -21,4 +21,4 @@ if err != nil {
 
 - [目录](directory.md)
 - 上一节:[网络和网页应用](18.9.md)
-- 下一节:[出于性能考虑的最佳实践和建议](18.11.md)
+- 下一节:[出于性能考虑的最佳实践和建议](18.11.md)

+ 1 - 1
eBook/18.2.md

@@ -47,4 +47,4 @@ Found: for row := range arr2Dim {
 
 - [目录](directory.md)
 - 上一节:[字符串](18.1.md)
-- 下一节:[映射](18.3.md)
+- 下一节:[映射](18.3.md)

+ 1 - 1
eBook/18.3.md

@@ -26,4 +26,4 @@ for key, value := range map1 {
 
 - [目录](directory.md)
 - 上一节:[数组和切片](18.2.md)
-- 下一节:[结构体](18.4.md)
+- 下一节:[结构体](18.4.md)

+ 1 - 1
eBook/18.4.md

@@ -31,4 +31,4 @@ func Newstruct1(n int, f float32, name string) *struct1 {
 
 - [目录](directory.md)
 - 上一节:[映射](18.3.md)
-- 下一节:[接口](18.5.md)
+- 下一节:[接口](18.5.md)

+ 1 - 1
eBook/18.5.md

@@ -35,4 +35,4 @@ func classifier(items ...interface{}) {
 
 - [目录](directory.md)
 - 上一节:[结构体](18.4.md)
-- 下一节:[函数](18.6.md)
+- 下一节:[函数](18.6.md)

+ 1 - 1
eBook/18.6.md

@@ -20,4 +20,4 @@ func protect(g func()) {
 
 - [目录](directory.md)
 - 上一节:[接口](18.5.md)
-- 下一节:[文件](18.7.md)
+- 下一节:[文件](18.7.md)

+ 1 - 1
eBook/18.7.md

@@ -49,4 +49,4 @@ func cat(f *file.File) {
 
 - [目录](directory.md)
 - 上一节:[函数](18.6.md)
-- 下一节:[协程(goroutine)与通道(channel)](18.8.md)
+- 下一节:[协程(goroutine)与通道(channel)](18.8.md)

+ 1 - 1
eBook/18.8.md

@@ -113,4 +113,4 @@ func Worker(in, out chan *Task) {
 
 - [目录](directory.md)
 - 上一节:[文件](18.7.md)
-- 下一节:[网络和网页应用](18.9.md)
+- 下一节:[网络和网页应用](18.9.md)

+ 1 - 1
eBook/18.9.md

@@ -18,4 +18,4 @@ var strTempl = template.Must(template.New("TName").Parse(strTemplateHTML))
 
 - [目录](directory.md)
 - 上一节:[协程(goroutine)与通道(channel)](18.8.md)
-- 下一节:[其他](18.10.md)
+- 下一节:[其他](18.10.md)

+ 1 - 1
eBook/19.0.md

@@ -1,4 +1,4 @@
-# 19 构建一个完整的应用程序
+# 19.0 构建一个完整的应用程序
 
 ## 链接
 

+ 11 - 11
eBook/directory.md

@@ -120,7 +120,7 @@
     - 12.6 [用切片读写文件](12.6.md)
     - 12.7 [用 defer 关闭文件](12.7.md)
     - 12.8 [使用接口的实际例子:fmt.Fprintf](12.8.md)
-    - 12.9 [格式化 JSON 数据](12.9.md)
+    - 12.9 [JSON 数据格式](12.9.md)
     - 12.10 [XML 数据格式](12.10.md)
     - 12.11 [用 Gob 传输数据](12.11.md)
     - 12.12 [Go 中的密码学](12.12.md)
@@ -137,10 +137,10 @@
     - 13.10 [性能调试:分析并优化 Go 程序](13.10.md)
 - 第14章:[协程(goroutine)与通道(channel)](14.0.md)
     - 14.1 [并发、并行和协程](14.1.md)
-    - 14.2 [使用通道进行协程间通信](14.2.md)
-    - 14.3 [协程同步:关闭通道-对阻塞的通道进行测试](14.3.md)
+    - 14.2 [协程间的信道](14.2.md)
+    - 14.3 [协程的同步:关闭通道-测试阻塞的通道](14.3.md)
     - 14.4 [使用 select 切换协程](14.4.md)
-    - 14.5 [通道超时和计时器(Ticker)](14.5.md)
+    - 14.5 [通道超时和计时器(Ticker)](14.5.md)
     - 14.6 [协程和恢复(recover)](14.6.md)
     - 14.7 [新旧模型对比:任务和worker](14.7.md)
     - 14.8 [惰性生成器的实现](14.8.md)
@@ -153,9 +153,9 @@
     - 14.15 [漏桶算法](14.15.md)
     - 14.16 [对Go协程进行基准测试](14.16.md)
     - 14.17 [使用通道并发访问对象](14.17.md)
-- 第15章:[网络、模与网页应用](15.0.md)
-    - 15.1 [tcp服务器](15.1.md)
-    - 15.2 [一个简单的web服务器](15.2.md)
+- 第15章:[网络、模与网页应用](15.0.md)
+    - 15.1 [tcp 服务器](15.1.md)
+    - 15.2 [一个简单的 web 服务器](15.2.md)
     - 15.3 [访问并读取页面数据](15.3.md)
     - 15.4 [写一个简单的网页应用](15.4.md)
     - 15.5 [确保网页应用健壮](15.5.md)
@@ -172,8 +172,8 @@
 - 第16章:[常见的陷阱与错误](16.0.md)
     - 16.1 [误用短声明导致变量覆盖](16.1.md)
     - 16.2 [误用字符串](16.2.md)
-    - 16.3 [发生错误时使用defer关闭一个文件](16.3.md)
-    - 16.4 [何时使用new()和make()](16.4.md)
+    - 16.3 [发生错误时使用 defer 关闭一个文件](16.3.md)
+    - 16.4 [何时使用 new()  make()](16.4.md)
     - 16.5 [不需要将一个指向切片的指针传递给函数](16.5.md)
     - 16.6 [使用指针指向接口类型](16.6.md)
     - 16.7 [使用值类型时误用指针](16.7.md)
@@ -181,7 +181,7 @@
     - 16.9 [闭包和协程的使用](16.9.md)
     - 16.10 [糟糕的错误处理](16.10.md)
 - 第17章:[模式](17.0.md)
-    - 17.1 [逗号ok模式](17.1.md)
+    - 17.1 [逗号 ok 模式](17.1.md)
     - 17.2 [defer 模式](17.2.md)
     - 17.3 [可见性模式](17.3.md)
     - 17.4 [运算符模式和接口](17.4.md)
@@ -197,7 +197,7 @@
     - 18.9 [网络和网页应用](18.9.md)
     - 18.10 [其他](18.10.md)
     - 18.11 [出于性能考虑的最佳实践和建议](18.11.md)
-- 第19章:构建一个完整的应用程序
+- 第19章:[构建一个完整的应用程序](19.0.md)
     - 19.1 [简介](19.1.md)
     - 19.2 [短网址项目简介](19.2.md)
     - 19.3 [数据结构](19.3.md)