Use combinations in go to implement inheritance

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

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

Interface embedding is very simple. We ' ve mentioned the IO. Reader and IO. Writer interfaces before; Here is their definitions.

type  reader  interface { Read  (p  []byte ) ( int , err  error )}   type   Writer  interface {write  ( p  []byte ) (n  Span class= "Hljs-title" >int , err  error )} 

The IO package also exports several and other interfaces this 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 other methods explicitly, but it's easier and more evocative to embed the both interfaces to form T He new one, like this:

ReadWriterReaderWriter interfaces.type ReadWriter interface {    Reader    Writer}

This says just what it looks like:a Readwriter can does 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 is embedded within interfaces.

The same basic idea applies to structs, but with more far-reaching implications. The BUFIO package has both 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.

ReadWriterReaderWriterIt implements io.ReadWriter.type ReadWriter struct {    *Reader  // *bufio.Reader    *Writer  // *bufio.Writer}

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

type ReadWriter struct {    reader *Reader    writer *Writer}

Promote the methods of the fields and to satisfy the IO interfaces, we would also need to provide forwarding M Ethods, like this:

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 and which means that bufio. Readwriter not only have the methods of Bufio. Reader and Bufio. Writer, it also satisfies all three interfaces:io. Reader, Io. Writer, and Io. Readwriter.

There's an important on which embedding differs from subclassing. When we embed a type, the methods of this type become methods of the outer type, but when they is invoked the receiver of The method is the inner type and not the outer one. In we example, when the Read method of a Bufio. Readwriter is invoked, it had exactly the same effect as the forwarding method written out above; The receiver is the reader field of the Readwriter, not the readwriter itself.

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

type Job struct {    Command string    *log.Logger}

The Job type now have the Log, Logf and other methods of *log. Logger. We could has 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("starting now...")

The Logger is a regular field of the job struct, so we can initialize it in the usual to inside the constructor for Job, Like this,

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, as it does in the Read method of our readerwriter struct. Here, if we needed to access the *log. Logger of a job variable job, we would write job. Logger, which would is useful if we wanted to refine the methods of Logger.

*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 is simple. First, a field or method x hides any and 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.

Second, if the same name appears at the same nesting level, it's usually an error; It would is 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 was added that conflicts with another field in another subtype if neither field is ever used .

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.