menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right All_wiki chevron_right the-way-to-go_ZH_CN chevron_right eBook chevron_right 10.2.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    10.2.md
    3.46 KB / 2024-07-16 23:14:27
        # 10.2 使用工厂方法创建结构体实例
    
    ## 10.2.1 结构体工厂
    
    Go 语言不支持面向对象编程语言中那样的构造子方法,但是可以很容易的在 Go 中实现 “构造子工厂”方法。为了方便通常会为类型定义一个工厂,按惯例,工厂的名字以 `new...` 或 `New...` 开头。假设定义了如下的 `File` 结构体类型:
    
    ```go
    type File struct {
        fd      int     // 文件描述符
        name    string  // 文件名
    }
    ```
    
    下面是这个结构体类型对应的工厂方法,它返回一个指向结构体实例的指针:
    
    ```go
    func NewFile(fd int, name string) *File {
        if fd < 0 {
            return nil
        }
    
        return &File{fd, name}
    }
    ```
    
    然后这样调用它:
    
    ```go
    f := NewFile(10, "./test.txt")
    ```
    
    在 Go 语言中常常像上面这样在工厂方法里使用初始化来简便的实现构造函数。
    
    如果 `File` 是一个结构体类型,那么表达式 `new(File)` 和 `&File{}` 是等价的。
    
    这可以和大多数面向对象编程语言中笨拙的初始化方式做个比较:`File f = new File(...)`。
    
    我们可以说是工厂实例化了类型的一个对象,就像在基于类的 OO 语言中那样。
    
    如果想知道结构体类型 `T` 的一个实例占用了多少内存,可以使用:`size := unsafe.Sizeof(T{})`。
    
    **如何强制使用工厂方法**
    
    通过应用可见性规则参考 [4.2.1节](04.2.md)、[9.5 节](09.5.md) 就可以禁止使用 `new()` 函数,强制用户使用工厂方法,从而使类型变成私有的,就像在面向对象语言中那样。
    
    ```go
    type matrix struct {
        ...
    }
    
    func NewMatrix(params) *matrix {
        m := new(matrix) // 初始化 m
        return m
    }
    ```
    
    在其他包里使用工厂方法:
    
    ```go
    package main
    import "matrix"
    ...
    wrong := new(matrix.matrix)     // 编译失败(matrix 是私有的)
    right := matrix.NewMatrix(...)  // 实例化 matrix 的唯一方式
    ```
    
    ## 10.2.2 map 和 struct vs new() 和 make()
    
    `new()` 和 `make()` 这两个内置函数已经在第 [7.2.4](07.2.md) 节通过切片的例子说明过一次。
    
    现在为止我们已经见到了可以使用 `make()` 的三种类型中的其中两个:
    
        slices  /  maps / channels(见第 14 章)
    
    下面的例子说明了在映射上使用 `new()` 和 `make()` 的区别以及可能发生的错误:
    
    示例 10.4 [new_make.go](examples/chapter_10/new_make.go)(不能编译)
    
    ```go
    package main
    
    type Foo map[string]string
    type Bar struct {
        thingOne string
        thingTwo int
    }
    
    func main() {
        // OK
        y := new(Bar)
        (*y).thingOne = "hello"
        (*y).thingTwo = 1
    
        // NOT OK
        z := make(Bar) // 编译错误:cannot make type Bar
        (*z).thingOne = "hello"
        (*z).thingTwo = 1
    
        // OK
        x := make(Foo)
        x["x"] = "goodbye"
        x["y"] = "world"
    
        // NOT OK
        u := new(Foo)
        (*u)["x"] = "goodbye" // 运行时错误!! panic: assignment to entry in nil map
        (*u)["y"] = "world"
    }
    ```
    
    试图 `make()` 一个结构体变量,会引发一个编译错误,这还不是太糟糕,但是 `new()` 一个 `map` 并试图向其填充数据,将会引发运行时错误! 因为 `new(Foo)` 返回的是一个指向 `nil` 的指针,它尚未被分配内存。所以在使用 `map` 时要特别谨慎。
    
    ## 链接
    
    - [目录](directory.md)
    - 上一节:[结构体定义](10.1.md)
    - 下一节:[使用自定义包中的结构体](10.3.md)
    
    
    links
    file_download