[翻譯] effective go 之 Embedding

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Embedding

Go does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.

Go沒有像其它物件導向語言中的類繼承概念 但是 它可以通過在結構體或者介面中嵌入其它的類型 來使用被嵌入類型的功能


Interface embedding is very simple. We've mentioned the io.Reader and io.Writer interfaces before; here are their definitions.

嵌入介面非常簡單 我們之前提到過io.Reader和io.Writer介面 下面是它們的定義:

type Reader interface {    Read(p []byte) (n int, err error)}type Writer interface {    Write(p []byte) (n int, err error)}


The io package also exports several other interfaces that specify objects that can implement several such methods. For instance, there is io.ReadWriter, an interface containing both Read and Write. We could specify io.ReadWriter by listing the two methods explicitly, but it's easier and more evocative to embed the two interfaces to form the new one, like this:

io包匯出了其它的介面 舉例來說 io.ReadWriter 這個介面包含了Read和Write 當然 我們可以直接定義io.ReadWriter介面 把相應的Write和Read函數直接定義其中 但是 嵌入介面的做法更好 可以利用已有的東西嘛 何必啥都親力親為:

// ReadWriter is the interface that combines the Reader and Writer interfaces.type ReadWriter interface {    Reader    Writer}

This says just what it looks like: A ReadWriter can do what a Reader does and what a Writer does; it is a union of the embedded interfaces (which must be disjoint sets of methods). Only interfaces can be embedded within interfaces.

ReadWriter同時提供Reader和Writer的功能 只有介面才能被嵌入到介面


The same basic idea applies to structs, but with more far-reaching implications. The bufio package has two struct types, bufio.Reader and bufio.Writer, each of which of course implements the analogous interfaces from package io. And bufio also implements a buffered reader/writer, which it does by combining a reader and a writer into one struct using embedding: it lists the types within the struct but does not give them field names.

struct可以嵌入其它類型 bufio包有兩個結構體類型 bufio.Reader 和 bufio.Writer 它們分別實現了和io包中Reader Writer類似功能的介面 bufio通過把reader和writer嵌入到同一個結構體中實現了緩衝式讀寫 這個結構體只列出了被嵌入的類型 但是沒有給相應的結構體欄位起名:

// ReadWriter stores pointers to a Reader and a Writer.// It implements io.ReadWriter.type ReadWriter struct {    *Reader  // *bufio.Reader    *Writer  // *bufio.Writer}

The embedded elements are pointers to structs and of course must be initialized to point to valid structs before they can be used. The ReadWriter struct could be written as

被嵌入的元素是指向結構體的指標 在使用前必須被初始化 ReadWriter結構體當然也可以寫成:

type ReadWriter struct {    reader *Reader    writer *Writer}

but then to promote the methods of the fields and to satisfy the io interfaces, we would also need to provide forwarding methods, like this:

但是 如果使用這樣給了欄位名的方式的話 在實現的時候就需要做一次轉換 把相應的操作傳遞給ReadWrtier.reader 或者ReadWriter.writer

func (rw *ReadWriter) Read(p []byte) (n int, err error) {    return rw.reader.Read(p)}


By embedding the structs directly, we avoid this bookkeeping. The methods of embedded types come along for free, which means that bufio.ReadWriter not only has the methods of bufio.Reader and bufio.Writer, it also satisfies all three interfaces: io.Reader, io.Writer, and io.ReadWriter.

通過直接嵌入結構體(沒有欄位名)可以避免在實現相應方法時做一次轉換 被嵌入類型的方法可以被直接使用 也就是說 bufio.ReadWriter不僅擁有了bufio.Reader bufio.Writer的方法 它同時也滿足了io.Reader io.Writer io.ReadWriter介面


There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one. In our example, when the Read method of a bufio.ReadWriter is invoked, it has exactly the same effect as the forwarding method written out above; the receiver is the reader field of the ReadWriter, not the ReadWriter itself.

嵌入和類繼承有一個重要的區別 我們可以嵌入類型 之後被嵌入類型方法就變成了我們的方法 但是 當這些方法被調用時 方法的接受者為被嵌入的類型 而不是我們自己的類型 在上述例子中 當bufio.ReadWriter的Read方法被調用時 它產生的效果 和我們之前定義的 有欄位名的方式(reader *Reader)一樣 把調用傳遞給了內部的類型


Embedding can also be a simple convenience. This example shows an embedded field alongside a regular, named field.

類型嵌入其實可以很簡單 看下面這段代碼 Job有一個直接嵌入的類型 *log.Logger和有欄位名的類型 Command string:

type Job struct {    Command string    *log.Logger}

The Job type now has the Log, Logf and other methods of *log.Logger. We could have given the Logger a field name, of course, but it's not necessary to do so. And now, once initialized, we can log to the Job:

Job現在有了*log.Logger的方法 例如Log Logf 現在 當它被初始化後 我們可以直接通過Job來使用Log:

job.Log("starting now...")

The Logger is a regular field of the struct and we can initialize it in the usual way with a constructor,

Logger是結構體的一個欄位 我們可以使用正常的初始化方式:

func NewJob(command string, logger *log.Logger) *Job {    return &Job{command, logger}}

or with a composite literal, 或者通過符合字面值初始化:

job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}


If we need to refer to an embedded field directly, the type name of the field, ignoring the package qualifier, serves as a field name. If we needed to access the *log.Loggerof a Job variable job, we would write job.Logger. This would be useful if we wanted to refine the methods of Logger.

如果我們需要顯式地引用被嵌入的欄位 可以利用不帶包首碼的欄位類型來達到目的 看下面這段代碼 我們想訪問Job變數job的*log.Loggerof欄位 我們可以寫成job.Logger 在需要重新定義Logger的方法很有用

func (job *Job) Logf(format string, args ...interface{}) {    job.Logger.Logf("%q: %s", job.Command, fmt.Sprintf(format, args...))}


Embedding types introduces the problem of name conflicts but the rules to resolve them are simple. First, a field or method X hides any other item X in a more deeply nested part of the type. If log.Logger contained a field or method called Command, the Command field of Job would dominate it.

類型嵌入會導致命名衝突的問題 但是解決的方法很簡單 首先 欄位或者方法X會隱藏掉被嵌套在深層次中的其它X 如果log.Logger包含一個Command欄位或者方法 那麼Job的最外層Command就會隱藏掉log.Logger裡的Command

Second, if the same name appears at the same nesting level, it is usually an error; it would be erroneous to embed log.Logger if the Job struct contained another field or method called Logger. However, if the duplicate name is never mentioned in the program outside the type definition, it is OK. This qualification provides some protection against changes made to types embedded from outside; there is no problem if a field is added that conflicts with another field in another subtype if neither field is ever used.

其次 如果相同的名字出現在了同一個嵌套層次中 通常會導致錯誤 如果Job已經有了另一個欄位或者方法為Logger 那麼再嵌套log.Logger可能就是錯誤的做法 但是 如果重名只出現在類型定義中 除了類型定義外的其它地方都不會引用到它 這是ok的 這種限制可以保護類型在其它地方被嵌入的更改 如果一個欄位被添加後會和其它子類型中的欄位衝突 但是這兩個衝突的欄位都沒有使用 那麼一切都OK

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.