不一样的go语言-不同的OO
前言
go语言因为产生时代的原因,大神们在设计go时,不得不考虑业界的流行趋势(编程理念),使得go既可以面向过程编程,也可以面向对象编程。这里不探讨两者的优劣,存在即是合理,面向过程编程经久不衰,而面向对象当今红红火火。如题所示,本文只计划聊一聊go的面向对象编程。
语法
面向对象离不开其三大特性,封装、继承、多态。那么go在语法层面是怎么实现这个的呢?
先来看一下封装,示例如下:
type House struct {//House是大写开头,公开类型,相当于java的public class
Name string //大写表示公开属性,即java的public
height float32 //小写表示私有属性,即java的private
width float32
}
//方法名大写表示公开方法,即java的public方法
func (h *House) Height() float32 {
return h.height
}
func (h *House) Width() float32 {
return h.width
}
func (h *House) Area() float32 {
return h.height * h.width
}
从上述代码可以看出,go语法的封装完全依赖属性名或方法名开头字母的大小写来声明,大写表示公开,小写表示私有。这无不体现go语言大道至简的特点。但在go语言中,它不叫封装,它叫导出。导出的类型、属性、方法在包外可见。
再看一下继承,示例如下:
package main
import "fmt"
func main() {
wh := WhiteHouse{House: House{height: 1.0, width: 2.0, name: "abc"}, Building: Building{name: "xyz"}}
//fmt.Println(wh.getName()) //此句会报多义的错误
fmt.Println(wh.height) //获得了House的属性
fmt.Println(wh.House.GetName()) //需要显式地指定调用哪个组合类型的方法
fmt.Println(wh.Building.GetName())
}
type House struct {
height float32
width float32
name string
}
func (h *House) GetName() string {
return h.name
}
type Building struct {
name string
}
func (b *Building) GetName() string {
return b.name
}
type WhiteHouse struct {
House //组合方式的继承,获得了House的属性及方法
Building //组合方式的继承,获得了Building的属性及方法
}
所谓继承,宽泛的理解应该是获得已有类型的属性或方法,从而达到代码简化及复用的目的。而往具体了说则是具有一定从属关系、父子关系或等级关系的对象之间的一种强关联的关系,目的不仅仅是为获得与复用,而是同时声明了一种关系。
关于继承,以下这几种语言的实现是最具有代表性的。
| 语言 | 继承方式 | 关键词 | 说明 |
|---|---|---|---|
| java | 单继承 | extends, interface | 规避类的复杂性及多义性;但仍可以通过接口实现多继承,可见下面的代码示例 |
| python | 多继承 | MRO,菱形继承,C3算法 | - |
| c++ | 多继承 | 虚基类, virtual | - |
| javascript | 原型链 | prototype,constructor | - |
| go | 组合继承 | embedded, duck | - |
java多继承示例:
public class Z {
public void z() {
}
}
public interface A {
void x();
}
public interface B {
void x();
}
//AB将会继承得到Z的方法z
//通过实现A、B两个接口的同名方法,得到自己专属的x方法实现,不存在多义的问题
public class AB extends Z implements A, B {
@Override
public void x() {
}
}
public interface C {
default void y() {
System.out.println("C->y");
}
}
public interface D {
default void y() {
System.out.println("D->y");
}
}
//此处会报编译错误,必须覆盖y方法。这是java从语言规范层面规避多义问题
public class CD extends Z implements C, D {
}
javascript多继承示例:
//以下代码摘自大神阮一峰《Javascript面向对象编程(二):构造函数的继承》一文
//在此拜谢
function Animal() {
this.species = "动物"
}
function Cat(name, color) {
this.name = name;
this.color = color;
}
//从这个函数里可以得知,javascript的继承有点简单粗暴的意思,相当于直接复制。
function Extend(Child, Parent) {
var F = function{};
F.prototype = Parent.prototype;
Child.prototype = F.prototype;
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
Extend(Cat, Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
而python与C++都支持多继承,只不过两者对多继承多义的处理不一样。前者是通过MRO解决,后者则是通过virtual关键字解决。
通过比较可以看出,go语言的继承是一种弱关系的继承,专心完成属性与方法的获得,以及代码复用。而这种继承方式也导致不支持继承类型间的转换,即上述代码中的WhiteHouse是不能转换为House或Building类型的。
在go中,如果要表达类似的强关系,只能通过interface来完成。但请注意,go的interface的概念依然不同于上述的java、c++语言,它不要求"实现了某个接口的类必须要实现接口中未实现的方法",而只是轻松写意地说道:"你只要实现这些个方法,那么你就实现了这些个接口"。 这两个说法有什么不同呢?虽然可以很明显地感觉后者的自由度更大,且看下文分解。
最后是多态。多态是同一个行为具有多个不同表现形式或形态的能力。体现在编程语言中就是同一个接口,在运行时因为不同的实例而执行不同操作。示例如下:
pckage main
import "fmt"
func main() {
//同一个对象g,不同的实例会有不同的表现(输出)
g := Machinegun{}
g.Fire()
g = Handgun{}
g.Fire()
}
type Gun interface {
Fire()
}
type Machinegun struct {
}
func (m *Machinegun) Fire() {
fmt.Println("machinegun fire")
}
type Handgun struct {
}
func (h *Handgun) Fire() {
fmt.Println("handgun fire")
}
在多态这一点上,存在接口概念的语言都有大体相同的代码写法。只是go还是一种支持鸭子类型的语言,尽管没有python这种动态类型语言做得那么淋漓尽致。请上代码如下:
python代码示例:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class Animal(object):
def __init__(self, name):
self.name = name
def walk(self):
print(self.name + "在散步")
class Cat(Animal):
pass
class Dog(Animal):
pass
class Abc(object):
def walk(self):
print("abc" + "在走路")
def hello(animal):
animal.walk()
if __name__ == "__main__":
c = Cat("小猫")
hello(c)
d = Dog("小狗")
hello(d)
//鸭子类型在这里体现得相当完美
//Abc并没有继承Animal,但却可以作为hello方法的参数
//而在静态语言中,就必须传递Animal的子类
abc = Abc()
hello(abc)
所谓鸭子类型,维基百科解释为:duck typing in computer programming is an application of the duck test — "If it walks like a duck and it quacks like a duck, then it must be a duck"。
鸭子类型得益于实现时不测试方法或函数中参数的类型,而依赖于文档以及清晰的代码和测试来保证正确使用,当然如果文档不完善,就只能靠查看代码才知道如何使用,这也是其缺点。
同时,鸭子类型使得Animal即使在后续的版本中增加方法,而旧的实现类仍未实现该新方法,升级版本后,也不会影响已有代码的使用。而这一点在以前的java中是做不到的,因而直接导致java 8接口中default语法的出现。
鸭子类型可以使得不用太关注对象类型,而转而关心对象的行为或能力。
那么在go中,鸭子类型是如何体现的呢?
package main
import "fmt"
func main() {
c := &Cat{}
hello(c)
abc := &Abc{}
hello(abc)
}
func hello(animal interface{}) {
animal.(Animal).walk()
}
type Animal interface {
walk()
//run()
}
type Cat struct {
}
func (c *Cat) walk() {
fmt.Println("小猫在散步")
}
type Abc struct {
}
func (a *Abc) walk() {
fmt.Println("abc在走路")
}
示例中的hello方法,因为interface{}神一般的存在,使得作为静态类型的go语言,可以模拟动态语言的写法。但似乎只是go真的只是鸭子类型语言而已,真的只是实现了鸭子类型的概念而已,其并没有动态语言的那种关于鸭子类型灵活性与自由度。
请关注公众号

不一样的go语言-不同的OO的更多相关文章
- OO,OO以后,及其极限
1.什么是软件开发? 软件开发的过程就是人们使用各种计算机语言将人们关心的现实世界映射到计算机世界的过程: 现在的计算机的数学理论基础是由计算机的开山鼻祖,大名鼎鼎的图灵于1937年提出的图灵机模型. ...
- 整理下.net分布式系统架构的思路
最近看到有部分招聘信息,要求应聘者说一下分布式系统架构的思路.今天早晨正好有些时间,我也把我们实际在.net方面网站架构的演化路线整理一下,只是我自己的一些想法,欢迎大家批评指正. 首先说明的是.ne ...
- 深入理解JavaScript系列:史上最清晰的JavaScript的原型讲解
一说起JavaScript就要谈的几个问题,原型就是其中的一个.说了句大话,史上最清晰.本来是想按照大纲式的行文写一下,但写到后边感觉其实就一个概念,没有什么条理性,所以下面就简单按照概念解释的模式谈 ...
- (转)yii流程,入口文件下的准备工作
yii流程 一 目录文件 |-framework 框架核心库 |--base 底层类库文件夹,包含CApplication(应用类,负责全局的用户请求处理,它管理的应用组件集, ...
- Python基础(11)--面向对象1
面向对象设计与面向对象编程的关系 面向对象设计(OOD)不会特别要求面向对象编程语言.事实上,OOD 可以由纯结构化语言来实现,比如 C,但如果想要构造具备对象性质和特点的数据类型,就需要在程序上作更 ...
- net分布式系统架构
net分布式系统架构的思路 最近看到有部分招聘信息,要求应聘者说一下分布式系统架构的思路.今天早晨正好有些时间,我也把我们实际在.net方面网站架构的演化路线整理一下,只是我自己的一些想法,欢迎大家批 ...
- 编译器的未来——我们还需要C++么?
在未来我们还需要纯C++开发模式么? 随着C++11的诞生,C++已经越来越臃肿,从03的时候就觉得C++实在是太复杂了.以一个合格C++程序员的标准来简单的来说3-5年略有小成,5-8年才可以说自己 ...
- Yii -- framework 目录结构说明
base 底层的类库文件 caching 所有缓存方法 cli 项目生成脚本 collecions 用PHP语言构造传统OO语言的数据存储单元.如队列,栈,哈希等等 console yii控制台 db ...
- Lambda in Java VS in C#
核心+变化 “凡是钱能解决的问题,就不是大问题.有很多问题是钱无法解决的,比如生老病死,比如不再相爱.”,看过<蜗居>的朋友一眼就能认出来.虽然这部电视剧讲的是chugui,但是毫无违和感 ...
随机推荐
- python 入门基础24 元类、单例模式
内容目录: 一.元类 二.单例模式 一.元类 1 什么是元类: 源自一句话:在python中,一切皆对象,而对象都是由类实例化得到的 class OldboyTeacher: def __init__ ...
- nrm安装与使用
1.什么是nrm nrm是一个npm源管理工具,使用它可以快速切换npm源. 2.安装 使用如下命令安装: npm install -g nrm 安装完后可使用 nrm -V 显示版本,注意是大写V. ...
- win10 + ubuntu双系统详细安装过程
由于搞深度学习,电脑跟不上,换了一台神舟战神Z8,于是装一个ubuntu双系统,没想到几乎花了一天,还花了80个软妹币找人帮忙,蓝瘦,现在写下来供大家参考: 不得不说,win10 + ubuntu双系 ...
- MIPI协议学习总结(一)
一.MIPI 简介: MIPI(移动行业处理器接口)是Mobile Industry Processor Interface的缩写.MIPI是MIPI联盟发起的为移动应用处理器制定的开放标准. 已经完 ...
- jrockit静默安装笔记
操作系统安装版本:CentOS-6.4-i386-minimal JDK安装版本:jrockit-jdk1.6.0_20-R28.1.0-4.0.1-linux-ia32 1.通过SecureFX工具 ...
- TCP/IP指纹鉴别 fingerprint
http://www.freebuf.com/articles/system/30037.html使用TCP/IP协议栈指纹进行远程操作系统辨识 Fyodor <fyodor@insecure. ...
- 『实践』Matlab实现Flyod求最短距离及存储最优路径
Matlab实现Flyod求最短距离及存储最优路径 一.实际数据 已知图中所有节点的X.Y坐标. 图中的节点编号:矩阵中的编号 J01-J62:1-62; F01-F60:63-122; Z01-Z0 ...
- C/C++杂记:虚函数的实现的基本原理
1. 概述 简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针.例: 其中: B的虚函数表中存放着B::foo和B ...
- 通过本地yum源安装软件报错[Errno 14] PYCURL ERROR 56 - "Failure when receiving data from the peer"
通过本地yum源安装软件报错 http://192.168.3.85/centos/6/os/x86_64/Packages/php-pdo-5.3.3-47.el6.x86_64.rpm: [Err ...
- CentOS 6.5环境下heartbeat高可用集群的实现及工作原理详解
Linux HA Cluster高可用服务器集群,所谓的高可用不是主机的高可用,而是服务的高可用. 什么叫高可用:一个服务器down掉的可能性多种多样,任何一个可能坏了都有可能带来风险,而服务器离线通 ...