从OOP的角度看Golang

资料来源

https://github.com/luciotato/golang-notes/blob/master/OOP.md?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

Golang中的概念

Golang中引入了在Golang中独有的struct 和 interface,其意义和传统语言中的不同。

概念清单

Golang Classic OOP
struct class with fields, only non-virtual methods
interface class without fields, only virtual methods
embedding multiple inheritance AND composition
receiver implicit this parameter

struct 是一个class但是没有virtual方法

struct 是一个class但是没有virtual方法,比如说:

type Rectangle struct {
    Name string
    Width, Height float64
}
func(r Rectangle) Area() float64 {
    return r.Width + r.Height
}

那么伪代码如下:

class Rectangle
  field Name: string
  field Width: float64
  field Height: float64
  method Area() //non-virtual
     return this.Width * this.Height

构造器

  • 对于每一个基本类型,默认使用0来填充,如果你没有显示的指定其初始值,那么就是0
  • 没有指定的类的构造器,只是对每一个类的实例,都有一个通用的构造器。使用类似于JSON格式的字面值常量来构造

function construct ( class,  literal)

  helper function assignFields ( object, literal)  //recursive
    if type-of object is "object"
        if literal has field-names
            for each field in object.fields
                if literal has-field field.name
                    assignFields(field.value,  literal.fields[field.name].value) //recurse
        else
        //literal without names, assign by position
            for n=0 to object.fields.length
                assignFields(object.fields[n].value,  literal.fields[n].value) //recurse
    else
        //atom type
        set object.value = literal.value

// generic constructor main body
var classInstance = new class
assignFields(classInstance, literal)
return classInstance

对于Golang的构造器使用


package main

import . "fmt"

type Rectangle struct {
    Name          string
    Width, Height float64
}
func main() {
    var a Rectangle
    var b = Rectangle{"I'm b.", 10, 20}
    var c = Rectangle{Height: 12, Width: 14}
    Println(a)
    Println(b)
    Println(c)
}

Golang中的嵌入类似于OOP中的没有virtual 方法的多继承

将一个结构体嵌入到另一个结构体,就好像是对非虚成员的多继承。让我们看看一个例子:

type base struct {
    a string
    b int
}
type derived  {
    base  //嵌入
    d int   
    a float32 //隐藏base中的a字段 
}

func main() {
    var x derived;
    fmt.Printf("%T", x.a) // x.a float32
    fmt.Printf("%T", x.base.a) //=> x. 
}

当base结构体嵌入到derived结构体中,那么base结构体中的字段和方法都可以在derived结构体中都可用。但是base内部的字段可以被derived中的字段shadowed。在内部,有一个名称叫base 类型为base的字段被创建,。如果将base嵌入到了derived,那么在derived类型可以直接访问base的字段。

Shadowing表示在derived中定义另一个与base 字段和方法相同名字的字段和方法,那么就会存在shadowinig这种情况。一但base中的字段被derived中的shadow,那么可以通过使用base名称来访问隐藏的字段。所有base中的字段,都可以通过隐藏字段的名称base来访问。需要注意的事情:

It is important to note that all inherited methods are called on the hidden-field-struct. It means that a base method cannot see or know about derived methods or fields. Everything is non-virtual.

当和结构体和嵌入这种概念打交道的时候,我们应该知道所有的东西都是在编译期间静态绑定的,所以的引用解析都是在静态编译期间。

When working with structs and embedding, everything is STATICALLY LINKED. All references are resolved at compile time.

多层嵌入

type NamedObj struct {
    Name string
}

type Shape struct {
    NameObj
    color string
    isRegular bool
}

type Point struct {
    x, y float64
}

type Rectangle struct {
    NameObj
    Shape
    center Point
    Width float64
    Height float64
}
func main() {
    var aRect = Rectange {
        NameObj{"name1"},
        Shape{NameObj{"name2"}, 0, true},
        Point{0, 0},
        20,
        2.5
    }
    fmt.Println(aRect.Name)
    fmt.Println(aRect.Shape)
    fmt.Println(aRect.Shape.Name)
}

等效的伪代码:

class NamedObj
   field Name: string

class Shape
   inherits NamedObj
   field color: int32
   field isRegular: bool

class Rectangle
   inherits NamedObj
   inherits Shape
   field center: Point
   field Width: float64
   field Height: float64

var aRect Rectangle中,

  • aRect.Name and aRect.NamedObj.Name 指的是同意对象
  • aRect.color and aRect.Shape.color 指的是同一个东西
  • aRect.name and aRect.NamedObj.name 指的是同一个字段, 但是 aRect.NamedObj.name 和 aRect.Shape.NamedObj.name 是不同的字段
  • aRect.NamedObj and aRect.Shape.NamedObj 是相同的类型,但是对象不同

方法隐藏

由于所有的golang-struct都是non-virtual的,所以你不可以覆盖这些方法。(当然接口是可以的)比如在NameObj中定义了一个名字叫做show的方法,但是呢,你在struct Rectangle中也定义了其他的方法,那么
Rectangle/show()将会隐藏NameObj/show方法。正如基类的字段一样,你也可以通过使用基类的名称来访问基类被隐藏的方法。

type base struct {
    a string
    b int
}
func (this base) xyz(){
    fmt.Println("xyz, a is ",   this.a);
}
// method display
func (this base) display() {
    fmt.Println("base, a is:", this.a)
}
type derived struct {
    base // embedding
    d    int
    a    float32 //-SHADOWED
}
// method display 
func(this derived) display() {
    fmt.Println("derived a is ", this.a)
}

func main() {
    var a derived = derived{base{"base-a", 10}, 20, 2.5}    
    a.display()  // 2.5
    a.base.diplay() // base, a is : base-a
    a.xyz(); // call base.xyz(), xyz, a is : base-a
}

Golang 方法和接受者

Golang的 struct-method,和类的non-virtual方法类似,但是在如下地方不同:

  • struct-method在struct外面定义
  • 既然起在struct body外面定义,那么在方法中有了一个名字叫做接受者
  • 对于这个接受者的名字,自己可以取一个简单的,一般是struct类型的第一个单词
type NameObj struct {
    Name string
}
func(n NameObj) show(){
    fmt.Println(n.Name)
}

struct 和 interface

Golang中的interface是一个没有字段的,只有虚方法。interface的设计是为了补充struct。在Golang中

**Structs: ** classes, with fields, ALL NON-VIRTUAL methods **Interfaces: ** classes, with NO fields, ALL VIRTUAL methods

通过严格限定struct是没有virtual的方法,interface是包含所有virtual的方法,通过嵌入来将两者结合起来,从而实现多态。

Interface

Golang interface 是一个没有字段的类,但是包含所有是virtual的函数。考虑到这种定义,那么你能够用interface干的事情:

  • 声明一个变量的类型为 type interface
  • 实现该interface,在一个struct中实现所有该interface定义的方法
  • 嵌入到另一个interface

空接口

在Golang中空接口是没有任何字段和没有任何方法,所以任何struct和基本类型都实现了interface{},这意味着var x interface{}可以容纳任何类型的值。那么我们可以使用interface{}来干什么事情呢?本来什么事情都不可以干,但是我们可以使用type switchtype assertion或反射

使用struct embedding

当你使用struct embedding,那么你就是要创建一个multi-root 体系,通过多继承。你要记住的是,所有的结构体方法都是non-virtual的。这就是为什么struct永远要比使用接口嵌入要快。因为没有接口涉及的时候,所有的函数的调用都是在编译期间就决定了。

原文地址:https://www.cnblogs.com/bofengqiye/p/6073943.html