面向对象编程 OOP

面向对象编程(OOP)是许多编程语言中的常见模式。面向对象的编程具有许多优点,例如可重用性,多态性和封装性。Go可以使用不同的方法来实现OOP的概念。 Go中支持OOP中的类,接口和方法,但是没有类型继承结构。

在Golang官方文档的“常见问题解答”中,有一个有趣的问题:“Go是一种面向对象的语言吗?”

指针

指针是Go中支持的寻址机制。 指针有两个运算符称为寻址(&)和取指针所指向的变量的值(*)。

package main
import (
    "fmt"
)
func main() {
    a := 42
    //prints out the value of a
    fmt.Println("value of a: ", a)

    //prints out the memory address that pointing to "a" variable
    fmt.Println("memory address of a: ", &a)
}

// output
// value of a:  42
// memory address of a:  0xc000014080

使用指针,变量可以寻址到另一个变量。 例如,b变量可以寻址到另一个变量(在此代码中,该变量称为a)。

package main
import (
    "fmt"
)
func main() {
    a := 42
    //prints out the value of a
    fmt.Println("value of a: ", a)

    //prints out the memory address that pointing to "a" variable
    fmt.Println("memory address of a: ", &a)

    b := &a
    //prints out the memory address of "b" variable
    fmt.Println("value of b:", b)

    //to prints out the value of b, use *  (dereference) operator
    fmt.Println("value of b using * operator: ", *b)
}

// output
// value of a:  42
// memory address of a:  0xc000014080
// value of b: 0xc000014080
// value of b using * operator:  42
指针图解

如果a的值更改,则*b的值也会更改。

//changes the value of "a"
a = 45
fmt.Println("value of a: ", a)
fmt.Println("value of *b: ", *b)

// output 
// value of a:  45
// value of *b:  45

struct

类是OOP的重要组成部分。 在Go中,有一个类似的概念称为struct。

// struct 声明
type structName struct {
    //field name type
    member int
    member2 string
    member3 []string
}

struct的使用示例。 说明了一个有说话能力的人。

//declare a struct called person
type person struct {
    name string
    age  int
}

//declare a method say() with type person as receiver
func (p person) say() {
    fmt.Println("Hello, my name is: ", p.name)
}

将已定义的struct分配给变量才能使用,如下所示:

package main

import "fmt"

//declare a struct called person
type person struct {
    name string
    age  int
}

//declare a method say() with type person as receiver
func (p person) say() {
    fmt.Println("Hello, my name is: ", p.name)
}

func main() {
    //实例化person,并赋值
    p1 := person{name: "Enki Gilbert", age: 42}
    //call a method say()
    p1.say()
}

// output
// Hello, my name is:  Enki Gilbert

Go中还提供了匿名struct。

s1 := struct{
    //declare some fields
    field1 int
    field2 []string
}{
    //instantiate directly
    field1: 12,
    field2: []string{"hi","mate"},
}

Composition

Go不支持继承,我们可以改用struct组合。

package main
import (
    "fmt"
)
//declare a struct called person
type person struct {
    name string
    age  int
}

//composition example
//declare a struct called manager that includes another struct called person
type manager struct {
    person
    team string
}

//declare a method say() with type person as receiver
func (p person) say() {
    fmt.Println("Hello, my name is: ", p.name)
}

func main() {
    //instantiate the person
    p1 := person{"Enki Gilbert", 42}

    //instantiate the manager and assign person field by using p1 variable
    m1 := manager{person: p1, team: "Racing Team Solvalou"}

    //the say() method is still available
    m1.say()
}
// output
// Hello, my name is:  Enki Gilbert

我们声明一个struct称为manager,person嵌入manager。say()方法仍可用于manager,因为它构成了另一个具有say()方法的struct。

实例化具有嵌套的struct的替代语法:

//instatiate the person struct into person field directly
m1 := manager{person: person{"Enki Gilbert", 42}, team: "Racing Team Solvalou"}

Interface

Interface是编程中的另一个强大概念。 Interface与struct类似,但只包含一些抽象方法。 在Go中,Interface定义了通用行为的抽象。

package main
import (
    "fmt"
)

//declare a rectangle struct
type rectangle struct {
    length int
    width  int
}

//declare an interface with area() as a member
type shape interface {
    area() int
}

//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
    return r.length * r.width
}

//declare a method with type shape as a parameter
func info(s shape) {
    fmt.Println("the area: ", s.area())
}

func main() {
    r1 := rectangle{12, 12}
    //r1 is a rectangle type. rectangle implements all methods in shape interface.
    info(r1)
}

// output
// the area:  144

根据该示例,我们声明一个矩形的struct和一个形状的interface。 矩形在形状interface中实现了area()。info()以形状类型作为参数。 实现了shape interface中所有方法的struct都可以作为info()的参数。

如果存在另一个struct,称为正方形。 info()方法也可用,因为正方形也可以实现shape接口中的所有方法。

package main

import (
    "fmt"
)

//declare a rectangle struct
type rectangle struct {
    length int
    width  int
}

//declare a square struct
type square struct {
    side int
}

//declare an interface with area() as a member
type shape interface {
    area() int
}

//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
    return r.length * r.width
}

//the square struct implements area() method in shape interface
func (s square) area() int {
    return s.side * s.side
}

//declare a method with type shape as a parameter
/**
anything that implements all methods in shape interface is considered as a shape in general.
for this case the rectangle and square is a shape because implements all methods in shape interface
**/
func info(s shape) {
    fmt.Println("the area: ", s.area())
}

func main() {
    r1 := rectangle{12, 12}
    info(r1)

    s1 := square{25}
    info(s1)
}

// output
// the area:  144
// the area:  625

另外值得注意的是,interface也可以组合。

Method sets

方法集是与特定类型关联的函数。 例如,area()是与shape类型相关联的函数。 如果方法集的接收者是一个值,那么该方法可以被一个值或一个值的指针使用。

例如,area()方法可用于shape的值和指针(在此示例中为矩形)。

package main
import (
    "fmt"
)

//shape interface
type shape interface {
    area() int
}
func (r rectangle) area() int {
    return r.length * r.width
}
//used in info() function
func info(s shape) {
    fmt.Println("the area: ", s.area())
}
func main() {
    r1 := rectangle{12, 12}
    //with value
    info(r1)
    //with pointer
    info(&r1)
}

// output
// the area:  144
// the area:  144

如果方法集的接收者是指针,则该方法仅能用指针调用。

package main
import (
    "fmt"
)

//the receiver is a pointer
func (r *rectangle) area() int {
    return r.length * r.width
}

func info(s shape) {
    fmt.Println("the area: ", s.area())
}

func main() {
    r1 := rectangle{12, 12}
    // 如果使用值调用,会报错
    // info(r1)
    //with pointer
    info(&r1)
}

// output
// the area:  144

深入学习

我希望本文对帮助学习Go编程语言有所帮助。 如果您有任何想法或反馈,可以在下面的评论留言。

关注公众号【技术全沾】学习更多有趣的编程知识。

Golang基础入门系列: