Open22

実用 Go言語 読書メモ

Daiki OkayamaDaiki Okayama

3章 構造体

Daiki OkayamaDaiki Okayama

3.3.3 インスタンスからメソッドを取り出して関数型として使う

構造体のインスタンスのメソッドは単体の関数と同じように利用できるが、レシーバーを経由して独自の名前空間にアクセスできる。

main.go
package main

type Struct struct {
	additionalField string
}

func (s *Struct) Method(arg string) {
	println(arg)
	println(s.additionalField)
}

func Func(arg string) {
	println(arg)
}

func HigherOrderFunc(f func(string), arg string) {
	f(arg)
}

func main() {
	var s Struct = Struct{additionalField: "additionalField"}

	HigherOrderFunc(s.Method, "method")
	HigherOrderFunc(Func, "function")
}

$ go run main.go
method
additionalField
function

s.MethodFunc は共に同じ型の引数と返り値を持つ関数であるが、s.Method の方が構造体のフィールドにアクセスできる分より多くの情報を持つ。

Daiki OkayamaDaiki Okayama

3.3.4 クロージャを使ってメソッドを再現する

上と同じことはクロージャを使っても実現できる。

main.go
package main

func Enclosure(additionalField string) func(arg string) {
	return func(arg string) {
		println(arg)
		println(additionalField)
	}
}

func Func(arg string) {
	println(arg)
}

func HigherOrderFunc(f func(string), arg string) {
	f(arg)
}

func main() {
	closure := Enclosure("additionalField")

	HigherOrderFunc(closure, "method?")
	HigherOrderFunc(Func, "function")
}

$ go run main.go
method?
additionalField
function
Daiki OkayamaDaiki Okayama

3.9.2 構造体の埋め込みは継承ではない

Python Pop Quiz 🐍❓

main.py
class Parent:
    def m1(self):
        self.m2()

    def m2(self):
        print("Parent")


class Child(Parent):
    def m2(self):
        print("Child")


c = Child()
c.m1()
c.m2()

答え

ChildParent から c1 を継承するが、Child.c1 実行時、self 引数は Child インスタンスを参照する。

$ python3 main.py
Child
Child

一方で、埋め込まれた構造体はそれ自身で閉じているため、オーバーライドはできない。

main.go
package main

type Parent struct {
}

func (p Parent) m1() {
	p.m2()
}

func (p Parent) m2() {
	println("Parent")
}

type Child struct {
	Parent
}

func (c Child) m2() {
	println("Child")
}

func main() {
	c := Child{}
	c.m1()
	c.m2()
}

$ go run main.go
Parent
Child
Daiki OkayamaDaiki Okayama

3.9.3 テンプレートメソッドパターンではなく、ストラテジーパターン

3.9.2 で見た通り、テンプレートメソッドパターンはそのままでは利用できない。

main.go
package main

type Character struct {
	x        int
	y        int
	moveImpl func(dx int, dy int)
}

func (c *Character) move(dx int, dy int) {
	println("各キャラクターにおける移動前の共通処理")
	c.moveImpl(dx, dy)
	println("各キャラクターにおける移動後の共通処理")
}

type Warrior struct {
	Character
}

func (w *Warrior) moveImpl(dx int, dy int) {
	w.x += dx * 2
	w.y += dy * 2
}

type Mage struct {
	Character
}

func (m *Mage) moveImpl(dx int, dy int) {
	m.x += dx
	m.y += dy
}

func main() {
	w := Warrior{}
	m := Mage{}

	w.move(1, 1) // panic!
	m.move(1, 1) // panic!
}

w.move()m.move() の呼び出しは、(&Warrior).moveImpl(&Mage)moveImpl ではなく、初期化されていない (&Character).moveImpl を参照するため、パニックを起こす。

$ go run main.go
各キャラクターにおける移動前の共通処理
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x1004de658]
...

構造体の初期化時に関数を与えることによって、振る舞いを部分的にカスタマイズすることができる。
(これはデリゲートパターン??)

main.go
package main

import "fmt"

type Character struct {
	x        int
	y        int
	moveImpl func(c *Character, dx int, dy int)
}

func (c *Character) move(dx int, dy int) {
	fmt.Println("各キャラクターにおける移動前の共通処理")
	c.moveImpl(c, dx, dy)
	fmt.Println("各キャラクターにおける移動後の共通処理")
}

func warriorMoveImpl(c *Character, dx int, dy int) {
	c.x += dx * 2
	c.y += dy * 2
}

func mageMoveImpl(c *Character, dx int, dy int) {
	c.x += dx
	c.y += dy
}

func main() {
	w := Character{moveImpl: warriorMoveImpl}
	m := Character{moveImpl: mageMoveImpl}

	w.move(1, 1)
	m.move(1, 1)

	println(w.x, w.y)
	println(m.x, m.y)
}
$ go run main.go
各キャラクターにおける移動前の共通処理
各キャラクターにおける移動後の共通処理
各キャラクターにおける移動前の共通処理
各キャラクターにおける移動後の共通処理
2 2
1 1
Daiki OkayamaDaiki Okayama

4章 インタフェース

Daiki OkayamaDaiki Okayama

4.5 実装を切り替えるためのさまざまな方法

実感が湧かなくて何を言ってるかわからない部分が多い。実際に動かして考えてみることにする。

Daiki OkayamaDaiki Okayama

4.5.1 基本の分岐

main.go
//go:build !darwin
// +build !darwin

package main

func main() {
	println("Hello, World! (not darwin)")
}

main_darwin.go
package main

func main() {
	println("Hello, World! (darwin)")
}

go.mod
module test

go 1.23.6
$ go build .
$ ./test
Hello, World! (darwin)
Daiki OkayamaDaiki Okayama

16章 エンタープライズなGoアプリケーションと並行処理