教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang 面试题(四)go的组合继承

golang 面试题(四)go的组合继承

发布时间:2022-02-08   编辑:jiaochengji.com
教程集为您提供golang 面试题(四)go的组合继承等资源,欢迎您收藏本站,我们将为您提供最新的golang 面试题(四)go的组合继承资源
<h1>1. 下面代码会输出什么?</h1> <pre><code class="language-Go">type People struct{} func (p *People) ShowA() { fmt.Println("showA") p.ShowB() } func (p *People) ShowB() { fmt.Println("showB") } type Teacher struct { People } func (t *Teacher) ShowB() { fmt.Println("teacher showB") } func main() { t := Teacher{} t.ShowA() }</code></pre>

考点:go的组合继承

解答:

这是Golang的组合模式,可以实现OOP的继承。 被组合的类型People所包含的方法虽然升级成了外部类型Teacher这个组合类型的方法(一定要是匿名字段),但它们的方法(ShowA())调用时接受者并没有发生变化。 此时People类型并不知道自己会被什么类型组合,当然也就无法调用方法时去使用未知的组合者Teacher类型的功能。

<pre><code>showA showB</code></pre>

@转载,https://zhuanlan.zhihu.com/p/47616970

 

<h1>2.浅谈继承和组合</h1> <blockquote>

面向对象编程讲究的是代码复用,继承和组合都是代码复用的有效方法。组合是将其他类的对象作为成员使用,继承是子类可以使用父类的成员方法。

引用一个生动的比方:<span style="color:#f33b45;">继承</span>是说“我父亲在家里给我帮了很大的忙”,<span style="color:#f33b45;">组合</span>是说“我请了个老头在我家里干活”。

继承

  在继承结构中,父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种“白盒式代码复用”。

优点:

<ul><li>简单易用,使用语法关键字即可轻易实现。</li><li>易于修改或扩展那些父类被子类复用的实现。</li></ul>

缺点:

<ul><li>编译阶段静态决定了层次结构,不能在运行期间进行改变。</li><li>破坏了封装性,由于“白盒”复用,父类的内部细节对于子类而言通常是可见的。</li><li>子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性。当父类的实现更改时,子类也不得不会随之更改。</li></ul>

组合

  组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以我们也说这种方式的代码复用是“黑盒式代码复用”。

优点:

<ul><li>通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合。</li><li>“黑盒”复用,被包含对象的内部细节对外是不可见。不破坏封装,整体类与局部类之间松耦合,彼此相对独立。</li><li>整体类对局部类进行包装,封装局部类的接口,提供新的接口,具有较好的可扩展性。</li></ul>

缺点:

<ul><li>整体类不能自动获得和局部类同样的接口,比继承实现需要的代码更多。</li><li>不熟悉的代码的话,不易读懂。</li></ul>

两者的选择

is-a关系用继承表达,has-a关系用组合表达。继承体现的是一种专门化的概念而组合则是一种组装的概念。

个人推荐:除非用到向上转型,不然优先考虑组合。

</blockquote>

@转载,https://blog.csdn.net/chuchen7021/article/details/100756291

 

<h1>3.如何理解go语言提倡组合,不提倡继承</h1>

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

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

<h3>组合与继承</h3>

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

<pre><code>继承的优缺点 优点: 1,类继承简单粗爆,直观,关系在编译时静态定义。 2,被复用的实现易于修改,sub可以覆盖super的实现。 缺点: 1,无法在运行时变更从super继承来的实现(也不一定是缺点) 2,sub的部分实现通常定义在super中。 3,sub直接面对super的实现细节,因此破坏了封装。 4,super实现的任何变更都会强制子类也进行变更,因为它们的实现联系在了一起。 5,如果在新的问题场景下继承来的实现已过时或不适用,所以必须重写super或继承来的实现。 由于在类继承中,实现的依存关系,对子类进行复用可能会有问题。有一个解决办法是,只从协议或抽象基类继承(子类型化),国为它们只对很少的实现,而协议则没有实现。 组合的优缺点 对象组合让我们同时使用多个对象,而每个对象都假定其他对象的接口正常运行。因此,为了在系统中正常运行,它们的接口都需要经过精心的设计。下面我就来说说他的优缺点 优点: 1,不会破坏封装,因为只通过接口来访问对象; 2,减少实现的依存关系,因为实面是通过接口来定义的; 3,可以在运行时将任意对象替换为其他同类型的对象; 4,可以保持类的封装以专注于单一任务; 5,类和他的层次结构能保持简洁,不至于过度膨胀而无法管理; 缺点: 1,涉及对象多; 2,系统的行为将依赖于不同对象间的关系,而不是定义于单个类中; 3,现成的组件总是不太够用,从而导致我们要不停的定义新对象。</code></pre>

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

<ol><li>可以利用面向接口编程原则的一系列优点,封装性好,耦合性低</li><li>相对于继承的编译期确定实现,组合的运行态指定实现,更加灵活</li><li>组合是非侵入式的,继承是侵入式的</li></ol><h3>理解golang的结构体嵌入</h3>

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

<h3>为什么嵌入语法是组合而非继承</h3>

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

<pre><code class="language-Go">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 }</code></pre>

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

<h3>活用组合和接口让代码更加优雅</h3>

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

<pre><code class="language-Go">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 }</code></pre>

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

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

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

<h3>与其他语言对比</h3>

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

<pre><code class="language-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(); }</code></pre>

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

@转载,https://www.jianshu.com/p/150523db21a9

 

<h1>4.golang 使用组合的方式实现继承</h1> <h3>摘要</h3>

<em>golang并非完全面向对象的程序语言,为了实现面向对象的继承这一神奇的功能,golang允许struct间使用匿名引入的方式实现对象属性方法的组合</em>

<h3>组合使用注意项</h3> <ul><li>使用匿名引入的方式来组合其他struct</li><li>默认优先调用外层方法</li><li>可以指定匿名struct以调用内层方法</li></ul><h3>代码示例</h3> <pre><code class="language-Go">package main import ( "fmt" ) type People struct{} type People2 struct{} func (p *People) ShowA() { fmt.Println("showA") p.ShowB() } func (p *People) ShowB() { fmt.Println("showB") } func (p *People) ShowC() { fmt.Println("showC") } func (p *People) ShowD() { fmt.Println("People:showD") } func (p *People2) ShowD() { fmt.Println("People2:showD") } type Teacher struct { People //组合People People2 //组合People2 } func (t *Teacher) ShowB() { fmt.Println("teacher showB") } func (t *Teacher) ShowC(arg string) { fmt.Println(arg) } func main() { t := Teacher{} //print showA //print showB t.ShowA() //print teacher showB t.ShowB() //print showB t.People.ShowB() //print test t.ShowC("test") //print showC t.People.ShowC() //因为组合方法中多次包含ShowD,所以调用时必须显示指定匿名方法 //print People2:showD t.People2.ShowD() }</code></pre>

@转载,https://www.jianshu.com/p/e70c3742404e

 

<h2> </h2>

 

 

 

到此这篇关于“golang 面试题(四)go的组合继承”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang 面试题(四)go的组合继承
golang基础教程
想系统学习GO语言(Golang
Go语言发展历史、核心、特性及学习路线
Go 开发关键技术指南 | 为什么你要选择 Go?(内含超全知识大图)
golang的继承机制
Golang中面向对象编程的继承机制解析
Go 语言到底适合干什么?
[GO语言基础] 一.为什么我要学习Golang以及GO语言入门普及
关于golang面向接口

[关闭]
~ ~