zoukankan      html  css  js  c++  java
  • Go 系列教程 ——第27篇:组合取代继承

    Go 不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。

    通过嵌套结构体进行组合

    在 Go 中,通过在结构体内嵌套结构体,可以实现组合。

    组合的典型例子就是博客帖子。每一个博客的帖子都有标题、内容和作者信息。使用组合可以很好地表示它们。通过学习本教程后面的内容,我们会知道如何实现组合。

    我们首先创建一个 author 结构体。

    package main
    
    import (  
        "fmt"
    )
    
    type author struct {  
        firstName string
        lastName  string
        bio       string
    }
    
    func (a author) fullName() string {  
        return fmt.Sprintf("%s %s", a.firstName, a.lastName)
    }

    在上面的代码片段中,我们创建了一个 author 结构体, author 的字段有 firstnamelastname 和 bio。我们还添加了一个 fullName() 方法,其中 author 作为接收者类型,该方法返回了作者的全名。

    下一步我们创建 post  结构体。

    type post struct {  
        title     string
        content   string
        author
    }
    
    func (p post) details() {  
        fmt.Println("Title: ", p.title)
        fmt.Println("Content: ", p.content)
        fmt.Println("Author: ", p.author.fullName())
        fmt.Println("Bio: ", p.author.bio)
    }

    post  结构体的字段有 title  和 content 。它还有一个嵌套的匿名字段 author。该字段指定 author  组成了 post  结构体。现在 post  可以访问 author  结构体的所有字段和方法。我们同样给 post  结构体添加了 details()  方法,用于打印标题、内容和作者的全名与简介。

    一旦结构体内嵌套了一个结构体字段,Go 可以使我们访问其嵌套的字段,好像这些字段属于外部结构体一样。所以上面第 11 行的 p.author.fullName()  可以替换为 p.fullName() 。于是,details()  方法可以重写,如下所示:

    func (p post) details() {  
        fmt.Println("Title: ", p.title)
        fmt.Println("Content: ", p.content)
        fmt.Println("Author: ", p.fullName())
        fmt.Println("Bio: ", p.bio)
    }

    现在,我们的 author 和 post 结构体都已准备就绪,我们来创建一个博客帖子来完成这个程序。

    package main
    
    import (  
        "fmt"
    )
    
    type author struct {  
        firstName string
        lastName  string
        bio       string
    }
    
    func (a author) fullName() string {  
        return fmt.Sprintf("%s %s", a.firstName, a.lastName)
    }
    
    type post struct {  
        title   string
        content string
        author
    }
    
    func (p post) details() {  
        fmt.Println("Title: ", p.title)
        fmt.Println("Content: ", p.content)
        fmt.Println("Author: ", p.fullName())
        fmt.Println("Bio: ", p.bio)
    }
    
    func main() {  
        author1 := author{        
            "Naveen",        
            "Ramanathan",        
            "Golang Enthusiast",
        }
        post1 := post{        
            "Inheritance in Go",        
            "Go supports composition instead of inheritance",
            author1,
        }
        post1.details()
    }

    在 playground 上运行

    在上面程序中,main 函数在第 31 行新建了一个 author 结构体变量。而在第 36 行,我们通过嵌套 author1 来创建一个 post。该程序输出:

    Title:  Inheritance in Go  
    Content:  Go supports composition instead of inheritance  
    Author:  Naveen Ramanathan  
    Bio:  Golang Enthusiast

    结构体切片的嵌套

    我们可以进一步处理这个示例,使用博客帖子的切片来创建一个网站。
    我们首先定义 website 结构体。请在上述代码里的 main 函数中,添加下面的代码,并运行它。

    type website struct {  
            []post
    }
    
    func (w website) contents() {  
        fmt.Println("Contents of Website
    ")    
        for _, v := range w.posts {
            v.details()
            fmt.Println()
        }
    }

    在你添加上述代码后,当你运行程序时,编译器将会报错,如下所示:

    main.go:31:9: syntax error: unexpected [, expecting field name or embedded type

    这项错误指出了嵌套的结构体切片 []post。错误的原因是结构体不能嵌套一个匿名切片。我们需要一个字段名。所以我们来修复这个错误,让编译器顺利通过。

    type website struct {  
            posts []post
    }

    可以看到,我给帖子的切片 []post 添加了字段名 posts

    现在我们来修改主函数,为我们的新网站创建一些帖子吧。

    修改后的完整代码如下所示:

    package main
    
    import (  
        "fmt"
    )
    
    type author struct {  
        firstName string
        lastName  string
        bio       string
    }
    
    func (a author) fullName() string {  
        return fmt.Sprintf("%s %s", a.firstName, a.lastName)
    }
    
    type post struct {  
        title   string
        content string
        author
    }
    
    func (p post) details() {  
        fmt.Println("Title: ", p.title)
        fmt.Println("Content: ", p.content)
        fmt.Println("Author: ", p.fullName())
        fmt.Println("Bio: ", p.bio)
    }
    
    type website struct {  
         posts []post
    }
    
    func (w website) contents() {  
        fmt.Println("Contents of Website
    ")    
        for _, v := range w.posts {
            v.details()
            fmt.Println()
        }
    }
        
    func main() {  
        author1 := author{        
            "Naveen",        
            "Ramanathan",        
            "Golang Enthusiast",
        }
        post1 := post{        
            "Inheritance in Go",        
            "Go supports composition instead of inheritance",
            author1,
        }
        post2 := post{        
            "Struct instead of Classes in Go",        
            "Go does not support classes but methods can be added to structs",
            author1,
        }
        post3 := post{        
            "Concurrency",        
            "Go is a concurrent language and not a parallel one",
            author1,
        }
        w := website{
            posts: []post{post1, post2, post3},
        }
        w.contents()
    }

    在 playground 中运行

    在上面的主函数中,我们创建了一个作者 author1,以及三个帖子 post1post2 post3。我们最后通过嵌套三个帖子,在第 62 行创建了网站 w,并在下一行显示内容。

    程序会输出:

    Contents of Website
    
    Title:  Inheritance in Go  
    Content:  Go supports composition instead of inheritance  
    Author:  Naveen Ramanathan  
    Bio:  Golang Enthusiast
    
    Title:  Struct instead of Classes in Go  
    Content:  Go does not support classes but methods can be added to structs  
    Author:  Naveen Ramanathan  
    Bio:  Golang Enthusiast
    
    Title:  Concurrency  
    Content:  Go is a concurrent language and not a parallel one  
    Author:  Naveen Ramanathan  
    Bio:  Golang Enthusiast
  • 相关阅读:
    EventLog实现事件日志操作
    可否控制<link type=text/css rel=stylesheet href=style.css>
    强制IE浏览器或WebBrowser控件使用指定版本显示网页2
    C#中的@符号
    C#运算符大全_各种运算符号的概述及作用
    调试时设置条件断点
    C语言:用字符读取流和输出流来读写入数据。(文本文件)
    建立完整的单向动态链表(包括初始化、创建、插入、删除、查找、销毁、输出)
    C语言:创建动态单向链表,创建完成后,输出每一个节点的数据信息。
    C语言:使用realloc函数对malloc或者calloc动态分配的内存大小进行扩展
  • 原文地址:https://www.cnblogs.com/marshhu/p/12272715.html
Copyright © 2011-2022 走看看