Go摒弃了许多OO的概念,但是还是很好的继承了OO的精髓——消息传递。我猜这个是学了Smalltalk的。通常我们说OO,我们会说这三大特性:对象,继承,多态。

1,Go中的对象

对于GO来说他的类型系统也分为两类:value type和reference type。value type就是内存中的内容,reference其实也是类似引用地址的指针。这样Go中的对象很自然的分为两类的:内容和引用内容的地址。Go的一个哲学就是让“天下没有隐晦的东西”。所以Go的对象在内存中的表示和其定义是一一对应的。有点“what you see is what you get”的感觉。

而在Go中,实现对象特征的是struct,其实这个好理解。当年C++之父Bjarne Stroustrup在从C语言捣腾C++的时候也是先扩展C中的struct,只不过,Bjarne Stroustrup太贪心,做的太多的。这个在学术上是有意义的,但在工程上并不买账。

Go中一切直白,没有隐藏的this指针。struct定义决定了struct内存的布局。但是Go添加了method,其实就是附加在struct上的一系列方法。我们可以通过struct的变量(相当我们OO中的实例)来调用这些方法。这些其实就是写语法糖而已。让原本C中的:

sayHello(Person *p, char *msg);

变成了类似于:

p := new(Person)

p.sayHello(msg string)

即使这小小的一变,在意义上变化大着了。这里的p是variable,Go称这样的variable为receiver。其实就是OO中的消息传递,而这个接受者就是p这个变量。

Go中有一点小的改进,调用语法上取消了对struct的变量和变量指针的区别。在上例中p := new(Person)中的p其实是new返回的指向Person变量的一个指针。但是我们仍然可以用点号来调用:p,sayHello()。

Go中的对象就这些,没有别的了。

2,Go中的继承

C++中的继承是很隐晦的,子类的constructor会自动调用父类的constructor,等等,等等。我们所谓的继承,其实是为了一种共享。这个共享,是通过嵌入来实现的。传统的OO语言中把这个工作交给编译器了,通过一些语法支持,编译器会创建一个父类对象的实例,然后通过类似base的指针去引用父类对象。好处就是创建时相对会简单,在工程上的缺点就是如果你不了解这些语法规则,那么你写的代码中有很多的坑——最可怕的是你并值知道有这些坑。

Go仍然是本着“all things are obvious”,组合就是组合,即把一个类型嵌入到另一个类型中。看个例子:

package main

import "fmt"

type Cellphone struct {
brand string
name string
number string
cost float32
} func (c *Cellphone) showNumber() {
fmt.Println("number:" + c.number)
} type Person struct {
Cellphone
name string
age int
} func (p *Person)showPersonalInfo() {
fmt.Printf("name:%s\tage:%d\t", p.name, p.age)
p.showNumber();
} func main() {
p := Person{Cellphone{"moto", "Defy", "+86-13844448888", 2000}, "jerry", 31} p.showNumber() fmt.Println(p.brand) //output:moto fmt.Println(p.name) //output:jerry p.showPersonalInfo() }

直接将一个类型定义中引用需要嵌入的类型。Go做了一些语法上的支持:

1,可以直接调用嵌入类型的方法,比如p.showNumber()。或是直接引用嵌入类型的字段p.brand

2,可以嵌入多个类型。例子中没有显示。

3,每个类型只能嵌入一次。

4,当嵌入的类型和当前类型类型有重名的时候,当前类型覆盖(重写?)嵌入(父类?)类型的字段。这个模拟了子类overwrite父类父类的功能。比如例子中的p.name显示的是Person这个struct的name。

5,当嵌入的类型超过一个,并且有字段有重名的情况。这时候我们可以明确地引用比如p.Cellphone.name。否则编译时会有类似:xxx ambigous的错误。

Go的继承也就这么多了,他的想法就是,让显性(obvious)的东西保持显性。即使使用了语法糖,也绝不在后面做小动作。

3,Go中的多态

短答案:Go中没有多态。面向对象中的多态其本质是方法的分发。通过类似于virtual table的东西记录下分发的规则。有复写的时候方法分发给当前实例(子类)方法,没有复写的时候分发给父类相应的方法。Go中不允许不同类型的赋值,所以从根本上不支持传统OO中的多态。

Go中通过interface来实现(契约式)方法的分发。interface定义了方法的签名,任意类型只要实现了该方法,我们就认为这个类型实现了这个接口。这个不需要在定义类型的时候指定该类型所要实现的接口的方式被称之为“非入侵式的接口(non-invasive interface)”。

Go虽然不容许不同类型间的赋值,但是接口是个例外(接口在Go的内部也是一种特别的type)。只要一个类型实现了该接口,我们就可以将该类型的变量赋值给该接口的变量:

//Interfaces
type Shaper interface {
Area() float32
} //Square
type Square struct {
border float32
} //we can assign the variable of Square to variable of Shaper(interface)
var ishaper Shaper = &Square{20.0}

这样Shaper的变量就可以引用任意一个实现了该接口类型的变量了。在底层接口变量其实是一个带着两个数据的指针:一块数据是该receiver(即引用的实际变量),另一块是该接口实现的方法表指针。借助于这点Go多态也就完善了。Go中称这个位type assertion。其实也就是OO中的类型推断。我们可以通过类似这样的语法去看变量是否实现了接口,或者是从接口中得到具体的类型:

ishaper = s  //assign a variable to interface variable
fmt.Println(ishaper.Area()) //tye assertion, get value from interface
if v, ok := ishaper.(*Square); ok {
fmt.Println(v)
} //type assertion to test value implement a interface
//type assertion is only applied on interface, so we cast the s to empty interface
if v, ok := interface{}(s).(Shaper); ok {
fmt.Println(v.Area())
}

好了,现在不难写出来体现多态的方法了,记住本质就是方法分发(method dispatch):

func PrintArea(s Shaper) {
fmt.Printf("The area is %v\n", s.Area())
}

如果你想实现可以接受任意参数的函数怎么办?Go中的空接口可以实现这个功能,空接口其实就是没有任何方法的接口:

type Any interface{

}

任何类型都实现了空接口,也就是说Any接口的变量可以引用任意一个其他类型的变量,在利用type assertion,我们可以写出通用的方法:

func PrintArea(s interface{}) {
if v, ok := s.(Shaper); ok {
fmt.Printf("The area is %v\n", v.Area())
}
}

完整示例:

package main

import "fmt"

//Interfaces
type Shaper interface {
Area() float32
} //Square
type Square struct {
border float32
} func (s *Square)Area() float32 {
return s.border * s.border
} //Rectangle
type Rectangle struct {
width float32
height float32
} func (r *Rectangle)Area() float32 {
return r.width * r.height
} //Polymorphism
func PrintArea(s interface{}) {
if v, ok := s.(Shaper); ok {
fmt.Printf("The area is %v\n", v.Area())
}
} func main() {
s := new(Square)
s.border = 2.0 var ishaper Shaper = s //assign a variable to interface variable
fmt.Println(ishaper.Area()) //tye assertion, get value from interface
if v, ok := ishaper.(*Square); ok {
fmt.Println(v)
} //type assertion to test value implement a interface
//type assertion is only applied on interface, so we cast the s to empty interface
if v, ok := interface{}(s).(Shaper); ok {
fmt.Println(v.Area())
} //Type switches
switch ishaper.(type) {
case *Square:
fmt.Println("Square type")
} r := &Rectangle{10.0, 15.0}
//a interface variable is a pointer in essence, so can pass the variable r(a pointer)
PrintArea(s)
PrintArea(r)
}

Go的多态也讲完了。

Go对OO的选择的更多相关文章

  1. SAP ALV OO 选择行打印

    &---------------------------------------------------------------------* *& Report  ZSDF001 * ...

  2. 选择语句=》OO函数实现

    let a; let b; if (a==="A") { b='定向' }else if (a==='B') { b='开放' }else if(a==='C') { b='全部' ...

  3. 如何选择PHP框架?

    PHP是世界上最受欢迎的编程语言之—.最近发布的PHP7令这种服务器的编程语言比以前变得更好,更稳定了. PHP被广泛应用于重大的项目.例如Facebook就是使用PHP来维护和创建它们的内部系统的. ...

  4. .net开发,html ajax开发架构之我见 bs ajax最简化法 Knock out Request,totally oo

    .net开发中,无论ajax还是webform,webpage, 总免不了要和request这个静态全局,可以远程通信的对象打交道. 而对于软件来讲,按照Matin Fowler的的面向对象,可利用软 ...

  5. ALV详解:OO SALV

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. ALV详解:OO ALV

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  7. ABAP OO的八大理由

    原贴地址:http://scnblogs.techweb.com.cn/abaplv/archives/127.html 几年前SAP BASIS 4.6为ABAP扩展了OO功能,这是很多传统的ABA ...

  8. OO ALV 学习参考

      http://blog.csdn.net/sapliumeng/article/details/18653491 一.ALV介绍 The ALV Grid Control (ALV = SAPLi ...

  9. OO真经——关于面向对象的哲学体系及科学体系的探讨(下)

    真经第六章——运作 Moving “运动是绝对的——牛顿” 6.1.导言 在前五章中,我们从世界观的这话题开始,逐步引出了抽象.层次.继承和耦合.这些内容,形成了对象论中关于世界的结构体系.      ...

随机推荐

  1. css中px,em和rem的区别

    css中px,em和rem的区别 今天,突然间发现一个特别有意思的问题,就是无意间看到一个网站中的em并不是16px,下面展开了对于px和em以及rem的探究. 首先,px是绝对长度单位,是相对于显示 ...

  2. .Net魔法堂:AssemblyInfo.cs文件详解

    一.前言 .net工程的Properties文件夹下自动生成一个名为AssemblyInfo.cs的文件,一般情况下我们很少直接改动该文件.但我们实际上通过另一个形式操作该文件.那就是通过在鼠标右键点 ...

  3. 连续值的CART(分类回归树)原理和实现

    上一篇我们学习和实现了CART(分类回归树),不过主要是针对离散值的分类实现,下面我们来看下连续值的cart分类树如何实现 思考连续值和离散值的不同之处: 二分子树的时候不同:离散值需要求出最优的两个 ...

  4. Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全

    导读 行文本输入框在用于界面的文本输入,在WEB登录表单中应用广泛.一般行文本编辑框可定制性较高,既可以当作密码输入框,又可以作为文本过滤器.QLineEdit本身使用方法也很简单,无需过多的设置就能 ...

  5. Hyperion Business Modeling for Microsoft Windows (32-bit)

    介质包搜索 常见问题    说明   复查 许可证列表 以确定需要下载的产品程序包. 请选择产品程序包和平台,然后单击“查找”. 如果只有一项结果,则可以看到下载页.如果有多个结果,请选择一个,然后单 ...

  6. Winform开发框架之权限管理系统的改进

    权限管理系统,一直是很多Mis系统和一些常见的管理系统所需要的,所以一般可以作为独立的模块进行开发,需要的时候进行整合即可,不需要每次从头开发,除非特殊的系统需求.我在Winform开发框架介绍中的随 ...

  7. 【jQuery基础学习】01 jQuery选择器

    关于CSS选择器 jQuery选择器涉及到CSS,CSS技术使得网页的结构与表现样式完全分离. 同样CSS也需要找到某个网页的结构才能改变其样式,这就是CSS选择器. 常用的CSS选择器如下: 标签选 ...

  8. SqlServer根据时段统计数据

    create table ST_PPTN_R_Real ( TID int primary key identity(1,1), STCD varchar(100) not null, TM date ...

  9. custom struts framework

    1. Difference between stucts1 and struts2 struts1 : Servlet used as Controller , you can visit the S ...

  10. Struts2中 Result类型配置详解

    一个result代表了一个可能的输出.当Action类的方法执行完成时,它返回一个字符串类型的结果码,框架根据这个结果码选择对应的result,向用户输出.在com.opensymphony.xwor ...