http://www.cnblogs.com/jasonxuli/p/6836399.html
 
传统 OOP 概念
 
OOP(面向对象编程)是对真实世界的一种抽象思维方式,可以在更高的层次上对所涉及到的实体和实体之间的关系进行更好的管理。
 
流传很广的OOP的三要素是:封装、继承、多态。
 
对象:可以看做是一些特征的集合,这些特征主要由 属性 和 方法 来体现。
封装:划定了对象的边界,也就是定义了对象。
继承:表明了子对象和父对象之间的关系,子对象是对父对象的扩展,实际上,子对象“是”父对象。相当于说“码农是人”。从特征的集合这个意义上说,子对象包含父对象,父对象有的公共特征,子对象全都有。
多态:根据继承的含义,子对象在特性上全包围了父对象,因此,在需要父对象的时候,子对象可以替代父对象。
 
传统的 OOP 语言,例如 Java,C++,C#,对OOP 的实现也各不相同。以 Java 为例: Java 支持 extends,也支持 interface。这是两种不同的抽象方式。
 
extends 就是继承,A extends B,表明 A 是 B 的一种,是概念上的抽象关系。
 
Class Human {
name:string
age:int
function eat(){}
function speak(){}
} Class Man extends Human {
function fish(){}
function drink(){}
}
Golang 的 OOP
 
回到 Golang。Golang 并没有 extends,它类似的方式是 Embedding。这种方式并不能实现 is-a 这种定义上的抽象关系,因此 Golang 并没有传统意义上的多态。
注意下面代码中的绿色粗体注释,把 Student 当做 Human 会报错。
 
package main

import "fmt"

func main(){
var h Human s := Student{Grade: 1, Major: "English", Human: Human{Name: "Jason", Age: 12, Being: Being{IsLive: true}}}
fmt.Println("student:", s)
fmt.Println("student:", s.Name, ", isLive:", s.IsLive, ", age:", s.Age, ", grade:", s.Grade, ", major:", s.Major) //h = s // cannot use s (type Student) as type Human in assignment
fmt.Println(h) //Heal(s) // cannot use s (type Student) as type Being in argument to Heal
Heal(s.Human.Being) // true s.Drink()
s.Eat()
} type Car struct {
Color string
SeatCount int
} type Being struct {
IsLive bool
} type Human struct {
Being
Name string
Age int
} func (h Human) Eat(){
fmt.Println("human eating...")
h.Drink()
} func (h Human) Drink(){
fmt.Println("human drinking...")
} func (h Human) Move(){
fmt.Println("human moving...")
} type Student struct {
Human
Grade int
Major string
} func (s Student) Drink(){
fmt.Println("student drinking...")
} type Teacher struct {
Human
School string
Major string
Grade int
Salary int
} func (s Teacher) Drink(){
fmt.Println("teacher drinking...")
} type IEat interface {
Eat()
} type IMove interface {
Move()
} type IDrink interface {
Drink()
} func Heal(b Being){
fmt.Println(b.IsLive)
}

输出结果:

student: {{{true} Jason 12} 1 English}
student: Jason , isLive: true , age: 12 , grade: 1 , major: English
{{false} 0}
true
student drinking...
human eating...
human drinking...

这里有一点需要注意,Student 实现了 Drink 方法,覆盖了 Human 的 Drink,但是没有实现 Eat 方法。因此,Student 在调用 Eat 方法时,调用的是 Human 的 Eat();而 Human 的 Eat() 调用了 Human 的 Drink(),于是我们看到结果中输出的是 human drinking... 。这既不同于 Java 类语言的行为,也不同于 prototype 链式继承的行为,Golang 叫做 Embedding,这像是一种寄生关系:Human 寄生在 Student 中,但仍保持一定程度的独立。

 
Golang 的接口
 
我们从接口产生的原因来考虑。
代码处理的是各种数据。对于强类型语言来说,非常希望一批数据都是单一类型的,这样它们的行为完全一致。但世界是复杂的,很多时候数据可能包含不同的类型,却有一个或多个共同点。这些共同点就是抽象的基础。单一继承关系解决了 is-a 也就是定义问题,因此可以把子类当做父类来对待。但对于父类不同但又具有某些共同行为的数据,单一继承就不能解决了。单一继承构造的是树状结构,而现实世界中更常见的是网状结构。
于是有了接口。接口是在某一个方面的抽象,也可以看做具有某些相同行为的事物的标签。
但不同于继承,接口是松散的结构,它不和定义绑定。从这一点上来说,Duck Type 相比传统的 extends 是更加松耦合的方式,可以同时从多个维度对数据进行抽象,找出它们的共同点,使用同一套逻辑来处理。
 
Java 中的接口方式是先声明后实现的强制模式,比如,你要告诉大家你会英语,并且要会听说读写,你才具有英语这项技能。
 
interface IEnglishSpeaker {
ListenEnglish()
ReadEnglish()
SpeakEnglish()
WriteEnglish()
}
 
Golang 不同,你不需要声明你会英语,只要你会听说读写了,你就会英语了。也就是实现决定了概念:如果一个人在学校(有School、Grade、Class 这些属性),还会学习(有Study()方法),那么这个人就是个学生。
Duck Type 更符合人类对现实世界的认知过程:我们总是通过认识不同的个体来进行总结归纳,然后抽象出概念和定义。这基本上就是在软件开发的前期工作,抽象建模。
相比较而言, Java 的方式是先定义了关系(接口),然后去实现,这更像是从上帝视角先规划概念产生定义,然后进行造物。
 
因为 interface 和 object 之间的松耦合,Golang 有 type assertion 这样的方式来判断一个接口是不是某个类型:
value, b := interface.(Type),value 是 Type 的默认实例;b 是 bool 类型,表明断言是否成立。
 
// 接上面的例子

v1, b := interface{}(s).(Car)
fmt.Println(v1, b) v2, b := interface{}(s).(Being)
fmt.Println(v2, b) v3, b := interface{}(s).(Human)
fmt.Println(v3, b) v4, b := interface{}(s).(Student)
fmt.Println(v4, b) v5, b := interface{}(s).(IDrink)
fmt.Println(v5, b) v6, b := interface{}(s).(IEat)
fmt.Println(v6, b) v7, b := interface{}(s).(IMove)
fmt.Println(v7, b) v8, b := interface{}(s).(int)
fmt.Println(v8, b)
 输出结果:
{ 0} false
{false} false
{{false} 0} false
{{{true} Jason 12} 1 English} true
{{{true} Jason 12} 1 English} true
{{{true} Jason 12} 1 English} true
<nil> false
0 false

上面的代码中,使用空接口 interface{} 对 s 进行了类型转换,因为 s 是 struct,不是 interface,而类型断言表达式要求点号左边必须为接口。

常用的方式应该是类似泛型的使用方式:

s1 := Student{Grade: 1, Major: "English", Human: Human{Name: "Jason", Age: 12, Being: Being{IsLive: true}}}
s2 := Student{Grade: 1, Major: "English", Human: Human{Name: "Tom", Age: 13, Being: Being{IsLive: true}}}
s3 := Student{Grade: 1, Major: "English", Human: Human{Name: "Mike", Age: 14, Being: Being{IsLive: true}}}
t1 := Teacher{Grade: 1, Major: "English", Salary: 2000, Human: Human{Name: "Michael", Age: 34, Being: Being{IsLive: true}}}
t2 := Teacher{Grade: 1, Major: "English", Salary: 3000, Human: Human{Name: "Tony", Age: 31, Being: Being{IsLive: true}}}
t3 := Teacher{Grade: 1, Major: "English", Salary: 4000, Human: Human{Name: "Ivy", Age: 40, Being: Being{IsLive: true}}}
drinkers := []IDrink{s1, s2, s3, t1, t2, t3} for _, v := range drinkers {
switch t := v.(type) {
case Student:
fmt.Println(t.Name, "is a Student, he/she needs more homework.")
case Teacher:
fmt.Println(t.Name, "is a Teacher, he/she needs more jobs.")
default:
fmt.Println("Invalid Human being:", t)
}
}
输出结果:
Jason is a Student, he/she needs more homework.
Tom is a Student, he/she needs more homework.
Mike is a Student, he/she needs more homework.
Michael is a Teacher, he/she needs more jobs.
Tony is a Teacher, he/she needs more jobs.
Ivy is a Teacher, he/she needs more jobs.

这段代码中使用了 Type Switch,这种 switch 判断的目标是类型。

  
Golang:接口为重
 
了解了 Golang 的 OOP 相关的基本知识后,难免会有疑问,为什么 Golang 要用这种“非主流”的方式呢?
 
 Java 之父 James Gosling 在某次会议上有过这样一次问答:
 
I once attended a Java user group meeting where James Gosling (Java’s inventor) was the featured speaker.
During the memorable Q&A session, someone asked him: “If you could do Java over again, what would you change?”  
“I’d leave out classes,” he replied. After the laughter died down, he explained that the real problem wasn’t classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible.
 
大意是:
问:如果你重新做 Java,有什么是你想改变的?
答:我会把类(class)丢掉。真正的问题不在于类本身,而在于基于实现的继承(the extends relationship)。基于接口的继承(the implements relationship)是更好的选择,你应该在任何可能的时候避免使用实现继承。
 
我的理解是:实现之间应该少用继承式的强关联,多用接口这种弱关联。接口已经可以在很多方面替代继承的作用,比如多态和泛型。而且接口的关系松散、随意,可以有更高的自由度、更多的抽象角度。
 
以继承为特点的 OOP 只是编程世界的一种抽象方式,在 Golang 的世界里没有继承,只有组合和接口,这看起来更符合 Gosling 的设想。借用那位老人的话:黑猫白猫,捉住老鼠就是好猫。让我来继续探索吧。
 
 
注:刚刚学习 Golang 不久,后面可能会发现也许某些理解是错误的。随时修正。
 

Golang OOP、继承、组合、接口的更多相关文章

  1. python面向对象编程 继承 组合 接口和抽象类

    1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均用点来访问自己的 ...

  2. Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类

    一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...

  3. Golang的面向对象编程【结构体、方法、继承、接口】

    Golang也支持面向对象编程.但与以前学过传统的面向对象编程语言有区别.1)Golang没有类class,Go语言的结构体struct和类class有相似的特性.2)Golang中不存在继承,方法重 ...

  4. Python 继承和组合 接口

    #解决代码重用的问题,减少代码冗余 #继承是一种什么'是'什么的关系 class People: def __init__(self, name, age): # print('People.__in ...

  5. 对java中继承、接口、组合的思考

    1.在c++中有继承和多重继承,而java中只有单继承.继承的好处在于可以复用一些东西,但缺陷在于后续不好扩展.此外,可读性方面继承也不好. 2.java中多了一个接口的概念,而接口的功能和其名字表达 ...

  6. GOLANG的继承+接口语法练习

    继承与接口同时存在 在Golang语言中,可以这么说:接口是继承的功能补充! 武当派有一个徒弟结构体,它继承WuDangMaster结构体的字段及方法 武林之中还有一个泰山北斗,名约少林派,少林入门神 ...

  7. 第一单元总结:基于基础语言、继承和接口的简单OOP

    前情提要 到目前为止,OO课程已经完成了前三次的作业,分别为: 第一次作业:简单多项式的构造和求导.[正则表达式][数据结构][排序] 第二次作业:含三角函数因子的复杂多项式的构造.求导和化简.[递归 ...

  8. GoLang之方法与接口

    GoLang之方法与接口 Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承.虚函数.构造函数和析构函数.隐藏的this指针等. 方法 Go 语言中同时有函数和方法.方法就是一个包含了接受者的函数 ...

  9. python 静态 封装 继承 mro 接口 super

    1.静态属性 静态方法  类方法 #!/usr/bin/python env # encoding: utf-8 # 静态属性 静态方法 class Room: tag = 168 def __ini ...

随机推荐

  1. Spring 4 官方文档学习(九)数据访问之事务管理

    说明:未整理版,未完待续,请绕行 本部分的重点是数据访问以及数据访问层与业务层之间的交互. 1.Spring框架的事务管理 介绍 http://docs.spring.io/spring/docs/c ...

  2. 关于Java方法的参数

    刚好看到C++的函数这块,说C++中除了引用类型的形参,其他都是实参的副本(个人总结). 隐约记得Java中方法的参数也是这么回事,于是手动测试一番. 结果 Java中方法的参数都是值传递,哪怕是引用 ...

  3. error: expected declaration specifiers or '...' before 'xxxx'(xxxx是函数形参)

    今天汗颜了一大阵 早上,在编译我的源代码的时候竟然不通过编译,上个星期六也出现了这种情况,当时不知道怎么弄的后来又通过编译了,可能是原来的.o文件没有make clean 还保存在那里,以至于蒙过去了 ...

  4. bootstrap -- css -- 辅助类

    文本 文本颜色 如果文本是个链接,则鼠标移动到链接文本上的时候,文本会变暗 .text-muted:灰色 .text-primary:浅蓝色 .text-success:绿色 .text-info:深 ...

  5. Eclipse/MyEclipse全屏插件

    此插件可以让Eclipse/MyEclipse的界面全屏,隐藏菜单栏和状态栏! MyEclipse 2014/2015中亲测有效! 插件下载: http://files.cnblogs.com/got ...

  6. 解决 SharePoint 2010 拒绝访问爬网内容源错误的小技巧(禁用环回请求的两种方式)

    这里有一条解决在SharePoint 2010搜索爬网时遇到的“拒绝访问错误”的小技巧. 首先要检查默认内容访问帐户是否具有相应的访问权限,或者添加一条相应的爬网规则.如果目标资源库是一个ShareP ...

  7. IPV6设置

    C:\Windows\System32\drivers\etc 目录下修改hosts文件. 网上有更新的ipv6 hosts文件,复制下来~ 别人不断更新的: https://raw.githubus ...

  8. python--数据类型--1

    原创博文,转载请标明出处--周学伟http://www.cnblogs.com/zxouxuewei/ 视频教程::http://www.imooc.com/learn/177 http://www. ...

  9. day04<Java语言基础+>

    Java语言基础(循环结构概述和for语句的格式及其使用) Java语言基础(循环结构for语句的练习之获取数据) Java语言基础(循环结构for语句的练习之求和思想) Java语言基础(循环结构f ...

  10. ios开发之--ios11适配:TableView的heightForHeaderInSection设置高度无效/UISearchBar消失

    更新到ios11,然后使用x-code9运行项目,发现tableview的-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInS ...