Go组合与继承

转载:原文链接

Golang是不支持继承的,因此我们在使用的时候往往使用组合。那么,组合与继承有什么区别呢?组合和继承都是面向对象编程中重要的概念。继承让一个类获得另一个类的属性和方法,形成层级关系,子类可以重用父类的功能。而组合则是将一个类的对象作为另一个类的成员变量,实现代码复用和模块化。继承建立的是"is-a"关系,表示一种类型的扩展;而组合则是"has-a"关系,表示对象之间的包含关系。相比继承,组合更加灵活,降低了类之间的耦合度,使得代码更易于维护和扩展。当然,Golang中,可以让父组件直接获取子组件的公开函数和公开属性。但是子组件和父组件之间依然是隔离的。

看一下如下例子:

package main
import "fmt"
type ClassA struct {}
func (a ClassA) Name() string {  return "ClassA"}
func (a ClassA) PrintName() {  fmt.Printf("I am %s", a.Name())}
type ClassB struct {  ClassA}
func (b ClassB) Name() string {  return "ClassB"}
func main() {  var obj = new(ClassB)  obj.PrintName()}

看似,我们想让classB继承classA,并重写了Name方法。但实际上,classB只是覆盖了Name方法,而没有重写他,对于classB里面的classA,其仍然与classB隔离。当classA访问Name方法时,不会用到classB的Name方法,依然是classA的Name方法。这一点与C++的非虚函数比较相似。这是组合很典型的一个特征:隔离。

为什么会写出上面的代码呢?因为我们误把组合当继承了。ClassB与ClassA的关系不是is-a,也就是ClassB不是ClassA的一种。相反,ClassB拥有了ClassA,他们是has-a关系。也就是ClassA的Name函数和CLassB的Name函数没有关系。就相当于菜篮子里面有苹果,菜篮子有自己的名字,苹果也有自己的名字。那么PrintName函数是属于ClassA的,他打印的是ClassA的名字,那当然和ClassB的Name没关系。只不过我们错误的使用了一些魔法,让原本应该为obj.ClassA.PrintName的方法变成了obj.PrintName。

那么,什么时候我们需要用到匿名属性(类似继承)的这种魔法呢?在我们定义一些结构体时,他们具有相同的属性,并且这些属性与其他属性相对较为隔离。

常见的,我们定义web的返回形参类型:

type Resp {  code int64  msg string}
type LoginResp {  Resp  Data {某种结构}}
type RegisterResp {  Resp  Data {某种结构}}

正如上面,Resp每次请求都会返回的内容,而且其与Data在行为上基本上没有关联。

Go在这方面确实与C更为接近,而不是与C++或者Java更为接近。

posted @ 2024-01-16 19:54  桓公子  阅读(29)  评论(0编辑  收藏  举报