美文网首页全端之路
如何理解go语言提倡组合,不提倡继承

如何理解go语言提倡组合,不提倡继承

作者: 刘凯_7013 | 来源:发表于2018-12-27 11:43 被阅读1167次

学习golang的过程中,有一个比较关注的价值观,golang提倡组合,不提倡继承。看过一些书和资料,感觉对这个概念的解释都不是很满意,特总结这篇文章,大家指正。

希望通过阅读本文,对网上的一些说法做纠正。比如 “golang是如何用组合实现继承的”,组合是组合,继承是继承,golang也没有想要混为一谈。应该说,golang是如何利用组合代替继承的~

组合与继承

先说说组合与继承的概念。对设计模式有过了解的同学对这两个名词应该都有初步的理解,我们来总结一下:
官方解释就不说了,组合一般理解为 has-a 的关系,继承是is-a的关系,两者都能起到代码复用的作用。以java为例,组合可以理解为类里边包含一个其他类型的属性值,继承是extends。
这里我引用一篇文章的段落浅谈组合与继承

继承的优缺点
优点:
1,类继承简单粗爆,直观,关系在编译时静态定义。
2,被复用的实现易于修改,sub可以覆盖super的实现。
缺点:
1,无法在运行时变更从super继承来的实现(也不一定是缺点)
2,sub的部分实现通常定义在super中。
3,sub直接面对super的实现细节,因此破坏了封装。
4,super实现的任何变更都会强制子类也进行变更,因为它们的实现联系在了一起。
5,如果在新的问题场景下继承来的实现已过时或不适用,所以必须重写super或继承来的实现。
由于在类继承中,实现的依存关系,对子类进行复用可能会有问题。有一个解决办法是,只从协议或抽象基类继承(子类型化),国为它们只对很少的实现,而协议则没有实现。

组合的优缺点
对象组合让我们同时使用多个对象,而每个对象都假定其他对象的接口正常运行。因此,为了在系统中正常运行,它们的接口都需要经过精心的设计。下面我就来说说他的优缺点
优点:
1,不会破坏封装,因为只通过接口来访问对象;
2,减少实现的依存关系,因为实面是通过接口来定义的;
3,可以在运行时将任意对象替换为其他同类型的对象;
4,可以保持类的封装以专注于单一任务;
5,类和他的层次结构能保持简洁,不至于过度膨胀而无法管理;
缺点:
1,涉及对象多;
2,系统的行为将依赖于不同对象间的关系,而不是定义于单个类中;
3,现成的组件总是不太够用,从而导致我们要不停的定义新对象。

总结来看,我认为,组合相对于继承的优点在于

  1. 可以利用面向接口编程原则的一系列优点,封装性好,耦合性低
  2. 相对于继承的编译期确定实现,组合的运行态指定实现,更加灵活
  3. 组合是非侵入式的,继承是侵入式的

理解golang的结构体嵌入

我们来参照代码理解下golang中的组合语法-结构体嵌入

为什么嵌入语法是组合而非继承

网上很少有例子解释清楚golang所提倡的组合的优势,一般就是将一个struct嵌入到另外一个struct里,这种仅仅是类似于继承提供的代码复用。

package main

import (
    "fmt"
)

type A struct {
}

func (*A) Hello(name string) {
    fmt.Println("hello " + name + ", i am a")
}

type B struct {
    *A
}

func main() {
    name := "Lee"
    a := A{}
    a.Hello(name) //hello Lee, i am a

    b := B{&A{}}
    b.Hello(name) //hello Lee, i am a

}

通过这个例子,我们先来理解为什么go语言的嵌入语法是组合而不是继承。
来看这个语句,b := B{&A{}}, b在赋值的时候,值语义里需要创建一个A类型的指针,赋值给B中的匿名变量。这就明显是has-a的关系了。

活用组合和接口让代码更加优雅

如上文所述,我认为组合需要与接口结合使用才能体现其精髓。
让我们来看一段改造后的代码:

package main

import (
    "fmt"
)

type IHello interface {
    Hello(name string)
}

type A struct {
}

func (*A) Hello(name string) {
    fmt.Println("hello " + name + ", i am a")
}

type D struct {
}

func (*D) Hello(name string) {
    fmt.Println("hello " + name + ", i am d")
}

type B struct {
    IHello
}

func (*B) Hello(name string) {
    fmt.Println("hello " + name + ", i am b")
}

type C struct {
    IHello
}

func main() {
    name := "Lee"
    a := A{}
    a.Hello(name) //hello Lee, i am a

    b := B{&A{}}
    b.Hello(name) //hello Lee, i am b

    b.IHello.Hello(name) //hello Lee, i am a

    c := C{&A{}}
    c.Hello(name) //hello Lee, i am a

    c.IHello = &D{}
    c.Hello(name) //hello Lee, i am d
}


发现不同了吗?我们来总结一下:

  1. A的指针继承了接口IHello, B,C中嵌入了接口IHello,
  2. B C两者在赋值时,同时可以根据运行时上下文指定其他具体实现,比如D,更加灵活。
  3. B中写了一个与IHello同名的方法Hello,此时直接访问b.Hello是访问的b的方法,想访问A的方法需要b.IHello.Hello(name)。我们可以把组合方式直接访问被嵌入类型方法看做一个语法糖。

所以让我们面向接口编程,提倡共用组合与接口的优雅代码

与其他语言对比

再补充一点便于理解go在组合上的努力。golang从语言级别对组合做了充分的语法糖,使得组合更加高效。我们来看一段java的组合实现

public  interface IHello {
        public void hello();
    }

    public class A implements IHello {
        @Override
        public void hello() {
            System.out.println("Hello, I am A.");
        }
    }

    public class B implements IHello {
        @Override
        public void hello() {
            System.out.println("Hello, I am B.");
        }
    }

    public class C {
        IHello h;

        public void hello() {
            h.hello();
        }
    }

    public static void main(String args[]) {
        C c = new C();
        c.h = new A();
        c.hello();
        c.h = new B();
        c.hello();
    }

例中类C组合了接口IHello, 如需暴露IHello的方法则需要添加一个代理方法,这样在代码量上会多于继承方式。golang中无需额外代码即可提供支持。

相关文章

  • 如何理解go语言提倡组合,不提倡继承

    学习golang的过程中,有一个比较关注的价值观,golang提倡组合,不提倡继承。看过一些书和资料,感觉对这个概...

  • 设计模式--装饰者模式思考

    装饰者模式实际上是一直提倡的组合代替继承的实践方式,个人认为要理解装饰者模式首先需要理解为什么需要组合代替继承,继...

  • <>

    结构体及其使用方法 结构体的定义 Go语言用嵌入式字段实现了继承吗?? GO语言不存在所谓的继承,只有组合。组合和...

  • react 组合vs继承

    react 提倡使用组合而不是继承来复用代码,具体原因呢,我现在还不知道-_-!好,先看看什么情况下我们会用到组合...

  • 提倡

    精益创业理念所提倡的三个克服分别是:1. 克服“过度准备”的惯性,向前一步,把未完成的事情完成。“先做好准备再上场...

  • Golang-TCP异步框架Tao分析

    TCP异步框架 Golang 编程风格 Go语言面向对象编程的风格是多用组合,少用继承,以匿名嵌入的方式实现继承。...

  • Go语言入门第三节 Go 语言面向对象机制

    Go语言面向对象编程 Go语言的面相对象和主流语言有很大的不同,如Go 语言里是不支持继承,Go语言的接口实现也不...

  • Go Web编程.epub

    【下载地址】 《Go Web编程》介绍如何用Go语言进行Web应用的开发,将Go语言的特性与Web开发实战组合到一...

  • Go中如何继承

    [TOC]在这里简单分享一下在Go中如何实现继承。 1. 简单的组合 说到继承我们都知道,在Go中没有extend...

  • Go语言一:Go语言究竟优秀在哪里

    Go语言继承了很多语言的精华,可以从2个维度来看。 语言的继承 语言的继承是说Go继承了不同语言的精华部分,而不是...

网友评论

    本文标题:如何理解go语言提倡组合,不提倡继承

    本文链接:https://www.haomeiwen.com/subject/fucrlqtx.html