Try   HackMD

Day 22 : Golang Struct(2)

前言

今天根據Go Data Structures這篇文章,再整理一些struct的特性。而有學過其它程式語言的話,會覺得這些特性不就是物件導向語言(Object-oriented programming)的特性嗎?沒錯,雖然Golang沒有"Class",也不完全算是個物件導向語言,但仍然可以有類似的設計模式,關於Golang如何實現OOP的特性,會在之後介紹完Interface再一起說明。

官方文件關於Go是否為OOP

Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

接下來會分別介紹文章中,Struct的四種特性

  • Encapsulation → state [“fields”] behaviour [“methods”] export / unexported
  • Reusability → Inheritance [“Embedded Types”]
  • Polymorphism → Interfaces
  • Overriding → Promotion

封裝(Encapsulation)

一個物件內部的屬性和方法是不可以被看見的被稱之為"Encapsulation",其中struct就是Golang裡面控制可見性的方法。簡單來說第一個字母為大寫的屬性和方法可以被其它package使用(exported),而小寫字母開頭的屬性和方法就只能在當前的package使用(unexported)。

以下面程式碼為例,User結構有三個fields:id,lastname,contact,其中contact是一個內嵌的結構體。print()函式為一個屬於User結構使用的方法,可以印出該型別變數的資訊,並建立兩個檔案,user/user.go、main.go。

在user.go定義了公開的User結構和私有的field和公共的方法Print()、UpdateName()。

// user/user.go
package user

import "fmt"

type User struct {
    id       int
    lastname string
    contact  ContactInfo
}

type ContactInfo struct {
    email   string
    zipCode int
}


func NewUser(id int, lastname string, email string, zipCode int) *User {
    contact := ContactInfo{email, zipCode}
    return &User{id, lastname, contact}
}


func (u *User) Print() {
    fmt.Printf("%v\n", u)
}

func (u *User) UpdateName(lastname string) {
    u.lastname = lastname
}

在main.go裡面先透過NewUser()函式建立一個新的User物件,並取得該物件的指標。之後就可以透過指標使用物件的方法,但在第11行英文lastaname是私有的,所以無法存取會發生錯誤。

//main.go package main import ( "playground/user" ) func main() { dylan:= user.NewUser(123,"Huang","dylan123@gmail.com",456) // fmt.Println(dylan.lastname) dylan.Print() // &{123 Huang {dylan123@gmail.com 456}} dylan.UpdateName("Wu") dylan.Print() // &{123 Wu {dylan123@gmail.com 456}} }

可重用性(Reusability)

Golang的struct支援支持嵌入式類型(Embedded Types),當把結構B嵌入到結構A時,B的欄位會被提升為A的結構,用這樣可以實現類似於繼承(Inheritance)的功能。

如這個例子,定義了ContactInfo和User兩個struct,然後將ContactInfo嵌入到了User中。接這創建了一個ContactInfo實例,並將其賦值給User的Contact屬性。最後,使用Printf()來輸出User的各個屬性值。

package main import "fmt" type User struct { id int lastname string contact ContactInfo } type ContactInfo struct { email string zipCode int } func main() { contact := ContactInfo{email: "dylan@gmail.com", zipCode: 12345} dylan := User{id: 1, lastname: "Huang", contact: contact} fmt.Printf("User ID: %d\n", dylan.id) // User ID: 1 fmt.Printf("User Zip Code: %d\n", dylan.contact.zipCode) // User Zip Code: 12345 }

多型(Polymorphism)

Polymorphism是一個物件可以使用不同的方法,在Go裡面通常是使用interfaces來定義一組方法,這個特色會在interface再說明。

覆寫(Overriding)

在可重用性的說明有提到,「當把結構B嵌入到結構A時,B的欄位會被提升為A的結構」,透過達成promotion,可透過點運算符號.進行操作。

References

  1. Go Data Structures
  2. DAY 18 Go 語言 自訂型別 (custom types) 及結構 (struct) 的定義
  3. 6.6. 封裝
  4. Go 语言实现封装,继承与多态
tags: About Go