Go是新旧想法的奇怪组合。 它具有一种非常令人耳目一新的方法,在该方法中,不怕放弃“做事”的既定观念。 许多人甚至不确定Go是否是一种面向对象的语言。 现在让我休息一下。 它是!
在本教程中,您将学习Go中所有复杂的面向对象设计,如何在Go中表达诸如封装,继承和多态性之类的面向对象程序设计的Struts,以及Go与其他语言的比较。
围棋设计理念
Go的根基基于C,更广泛地基于Algol家族 。 肯·汤普森开玩笑地说,罗伯·派克,罗伯特·格兰杰和他自己聚在一起 ,决定他们讨厌C ++。 不管是不是在开玩笑,Go与C ++都有很大的不同。 以后再说。 Go是关于最终的简单性。 罗伯•派克(Rob Pike)在“ 少即是更多”中对此进行了详细解释。
Go与其他语言
Go没有类,没有对象,没有异常,也没有模板。 它具有垃圾回收和内置并发。 就面向对象而言,最明显的遗漏是Go中没有类型层次结构。 这与大多数面向对象的语言(如C ++,Java,C#,Scala),甚至动态语言(如Python和Ruby)形成对比。
面向对象的语言功能
Go没有类,但是有类型。 特别是它具有结构。 结构是用户定义的类型。 结构类型(带有方法)的作用与其他语言中的类相似。
结构
结构定义状态。 这是一个生物结构。 它有一个“名称”字段和一个称为“真实”的布尔标志,它告诉我们它是真实的生物还是虚构的生物。 结构只保留状态,没有行为。
type Creature struct {
Name string
Real bool
}
方法
方法是对特定类型进行操作的函数。 他们有一个接收者条款,规定了他们要使用哪种类型。 这是一个对Creature结构进行操作并显示其状态的Dump()
方法:
func (c Creature) Dump() {
fmt.Printf("Name: '%s', Real: %t\n", c.Name, c.Real)
}
这是一种不寻常的语法,但是它非常显式和清晰(不同于隐式的“ this”或Python令人困惑的“ self”)。
嵌入
您可以将匿名类型彼此嵌入。 如果您嵌入无名结构,则嵌入式结构将其状态(和方法)直接提供给嵌入结构。 例如, FlyingCreature
中嵌入了一个无名的Creature
结构,这意味着FlyingCreature
是Creature
。
type FlyingCreature struct {
Creature
WingSpan int
}
现在,如果您有FlyingCreature的实例,则可以直接访问其Name和Real属性。
dragon := &FlyingCreature{
Creature{"Dragon", false, },
15,
}
fmt.Println(dragon.Name)
fmt.Println(dragon.Real)
fmt.Println(dragon.WingSpan)
介面
接口是Go面向对象支持的标志。 接口是声明方法集的类型。 与其他语言的接口类似,它们没有实现。
实现所有接口方法的对象会自动实现接口。 没有继承,子类或“实现”关键字。 在下面的代码片段中,类型Foo实现Fooer接口(按照约定,Go接口名称以“ er”结尾)。
type Fooer interface {
Foo1()
Foo2()
Foo3()
}
type Foo struct {
}
func (f Foo) Foo1() {
fmt.Println("Foo1() here")
}
func (f Foo) Foo2() {
fmt.Println("Foo2() here")
}
func (f Foo) Foo3() {
fmt.Println("Foo3() here")
}
面向对象的设计:前进的道路
让我们看看Go如何与面向对象编程的Struts进行对立:封装,继承和多态。 这些是基于类的编程语言的功能,它们是最流行的面向对象的编程语言。
对象的核心是具有状态和行为的语言构造,这些状态和行为可对状态进行操作并将其有选择地公开给程序的其他部分。
封装形式
Go在包级别封装了东西。 以小写字母开头的名称仅在该程序包中可见。 您可以将任何内容隐藏在私有包中,而仅公开特定的类型,接口和工厂功能。
例如,在此处隐藏上面的Foo
类型并仅公开接口,您可以将其重命名为小写foo
并提供NewFoo()
返回公共Fooer接口的函数:
type foo struct {
}
func (f foo) Foo1() {
fmt.Println("Foo1() here")
}
func (f foo) Foo2() {
fmt.Println("Foo2() here")
}
func (f foo) Foo3() {
fmt.Println("Foo3() here")
}
func NewFoo() Fooer {
return &Foo{}
}
然后来自另一个包的代码可以使用NewFoo()
并访问由内部foo
类型实现的Fooer
接口:
f := NewFoo()
f.Foo1()
f.Foo2()
f.Foo3()
遗产
继承或子类化始终是一个有争议的问题。 实现继承有很多问题(与接口继承相反)。 用C ++和Python及其他语言实现的多重继承遭受致命的死亡问题的困扰,但即使是单一继承也不会与脆弱的基类问题发生冲突。
现在,现代语言和面向对象的思想更偏重于继承而不是继承。 Go令人振奋,并且没有任何类型层次结构。 它允许您通过组合共享实现细节。 但是Go出现了一个非常奇怪的转折(可能源自实用性问题),它允许通过嵌入进行匿名合成。
对于所有意图和目的,通过嵌入匿名类型进行的组合等效于实现继承。 嵌入式结构与基类一样脆弱。 您还可以嵌入接口,这等效于以Java或C ++等语言从接口继承。 如果嵌入类型未实现所有接口方法,则甚至可能导致在编译时未发现运行时错误。
SuperFoo在此嵌入了Fooer接口,但未实现其方法。 Go编译器将很乐意让您创建一个新的SuperFoo并调用Fooer方法,但是显然会在运行时失败。 这样编译:
type SuperFooer struct {
Fooer
}
func main() {
s := SuperFooer{}
s.Foo2()
运行此程序会导致恐慌:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x28 pc=0x2a78]
goroutine 1 [running]:
panic(0xde180, 0xc82000a0d0)
/usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:464 +0x3e6
main.main()
/Users/gigi/Documents/dev/go/src/github.com/oop_test/main.go:104 +0x48
exit status 2
Process finished with exit code 1
多态性
多态是面向对象编程的本质:只要对象坚持同一接口,就可以统一对待不同类型的对象。 Go界面以非常直接和直观的方式提供了此功能。
这是一个精心制作的示例,其中创建了实现Dumper接口的多个生物(和一扇门!)并将其存储在一个切片中,然后为每个生物调用Dump()
方法。 您还会注意到实例化对象的不同样式。
package main
import "fmt"
type Creature struct {
Name string
Real bool
}
func Dump(c*Creature) {
fmt.Printf("Name: '%s', Real: %t\n", c.Name, c.Real)
}
func (c Creature) Dump() {
fmt.Printf("Name: '%s', Real: %t\n", c.Name, c.Real)
}
type FlyingCreature struct {
Creature
WingSpan int
}
func (fc FlyingCreature) Dump() {
fmt.Printf("Name: '%s', Real: %t, WingSpan: %d\n",
fc.Name,
fc.Real,
fc.WingSpan)
}
type Unicorn struct {
Creature
}
type Dragon struct {
FlyingCreature
}
type Pterodactyl struct {
FlyingCreature
}
func NewPterodactyl(wingSpan int) *Pterodactyl {
pet := &Pterodactyl{
FlyingCreature{
Creature{
"Pterodactyl",
true,
},
wingSpan,
},
}
return pet
}
type Dumper interface {
Dump()
}
type Door struct {
Thickness int
Color string
}
func (d Door) Dump() {
fmt.Printf("Door => Thickness: %d, Color: %s", d.Thickness, d.Color)
}
func main() {
creature := &Creature{
"some creature",
false,
}
uni := Unicorn{
Creature{
"Unicorn",
false,
},
}
pet1 := &Pterodactyl{
FlyingCreature{
Creature{
"Pterodactyl",
true,
},
5,
},
}
pet2 := NewPterodactyl(8)
door := &Door{3, "red"}
Dump(creature)
creature.Dump()
uni.Dump()
pet1.Dump()
pet2.Dump()
creatures := []Creature{
*creature,
uni.Creature,
pet1.Creature,
pet2.Creature}
fmt.Println("Dump() through Creature embedded type")
for _, creature := range creatures {
creature.Dump()
}
dumpers := []Dumper{creature, uni, pet1, pet2, door}
fmt.Println("Dump() through Dumper interface")
for _, dumper := range dumpers {
dumper.Dump()
}
}
结论
Go是一种真正的面向对象编程语言。 它支持基于对象的建模,并促进了使用接口而不是具体类型层次结构的最佳实践。 Go做出了一些不寻常的语法选择,但是总体上使用类型,方法和接口感觉很简单,轻巧,自然。
嵌入不是很纯净,但显然实用主义正在发挥作用,并且提供了嵌入,而不是仅提供名称上的组合。
翻译自: https://code.tutsplus.com/tutorials/lets-go-object-oriented-programming-in-golang--cms-26540