Slide 1

Slide 1 text

GoとPHPの インターフェイスの違い 2025/02/19 Go Connect #5 しまぶ@shimabox


Slide 2

Slide 2 text

自己紹介 { "NAME": "しまぶ", "SNS": "@shimabox", "TAMASHII": "沖縄", "COMPANY": "カオナビ", "SKILL": [ "PHP", "Go" ] } whoami.json

Slide 3

Slide 3 text

自己紹介 📖 共著で本を書きました 📖

Slide 4

Slide 4 text

自己紹介 📖 共著で本を書きました 📖 ひとり8冊買ってくれ!

Slide 5

Slide 5 text

自己紹介 📖 共著で本を書きました 📖 心はPHPer、身体はGopher。

Slide 6

Slide 6 text

1. GoとPHPのインターフェイス 2. Goのインターフェイスを少し深ぼる 3. サンプルを交えてさらに深ぼる 4. まとめ アジェンダ

Slide 7

Slide 7 text

1. GoとPHPのインターフェイス 2. Goのインターフェイスを少し深ぼる 3. サンプルを交えてさらに深ぼる 4. まとめ GoとPHPのインターフェイス

Slide 8

Slide 8 text

GoとPHPのインターフェイス まず、PHPのインターフェイス interface LoggerInterface { public function log(string $message): void; } class FileLogger implements LoggerInterface { public function log(string $message): void { echo 'Logging to file: ' . $message; } } function logMessage(LoggerInterface $logger) { $logger->log('Hello, PHP!'); } logMessage(new FileLogger());

Slide 9

Slide 9 text

GoとPHPのインターフェイス まず、PHPのインターフェイス interface LoggerInterface { public function log(string $message): void; } class FileLogger implements LoggerInterface { public function log(string $message): void { echo 'Logging to file: ' . $message; } } function logMessage(LoggerInterface $logger) { $logger->log('Hello, PHP!'); } logMessage(new FileLogger()); インターフェイスの定義

Slide 10

Slide 10 text

GoとPHPのインターフェイス まず、PHPのインターフェイス interface LoggerInterface { public function log(string $message): void; } class FileLogger implements LoggerInterface { public function log(string $message): void { echo 'Logging to file: ' . $message; } } function logMessage(LoggerInterface $logger) { $logger->log('Hello, PHP!'); } logMessage(new FileLogger()); インターフェイスの実装

Slide 11

Slide 11 text

GoとPHPのインターフェイス まず、PHPのインターフェイス interface LoggerInterface { public function log(string $message): void; } class FileLogger implements LoggerInterface { public function log(string $message): void { echo 'Logging to file: ' . $message; } } function logMessage(LoggerInterface $logger) { $logger->log('Hello, PHP!'); } logMessage(new FileLogger()); インターフェイスに依存

Slide 12

Slide 12 text

GoとPHPのインターフェイス まず、PHPのインターフェイス interface LoggerInterface { public function log(string $message): void; } class FileLogger implements LoggerInterface { public function log(string $message): void { echo 'Logging to file: ' . $message; } } function logMessage(LoggerInterface $logger) { $logger->log('Hello, PHP!'); } logMessage(new FileLogger()); Love注入

Slide 13

Slide 13 text

GoとPHPのインターフェイス まず、PHPのインターフェイス interface LoggerInterface { public function log(string $message): void; } class FileLogger implements LoggerInterface { public function log(string $message): void { echo 'Logging to file: ' . $message; } } function logMessage(LoggerInterface $logger) { $logger->log('Hello, PHP!'); } logMessage(new FileLogger()); Love注入 (LoggerInterfaceを実 装しているものであれば 注入できる)

Slide 14

Slide 14 text

GoとPHPのインターフェイス まず、PHPのインターフェイス 明示的な実装 ● `implements` で明示的にインターフェイスを実装 ● どのクラスがどのインターフェイスを実装しているかが 明確

Slide 15

Slide 15 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) }

Slide 16

Slide 16 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) } インターフェイスの定義

Slide 17

Slide 17 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) } FileLogger 構造体

Slide 18

Slide 18 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) } FileLoggerは インターフェイスを 満たしている

Slide 19

Slide 19 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) } インターフェイスに依存

Slide 20

Slide 20 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) } Love注入

Slide 21

Slide 21 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス type LoggerInterface interface { Log(message string) } type FileLogger struct{} func (f FileLogger) Log(message string) { fmt.Println("Logging to file:", message) } func logMessage(logger LoggerInterface) { logger.Log("Hello, Go!") } func main() { logMessage(FileLogger{}) } Love注入 (LoggerInterfaceを満 たしているものであれば 注入できる)

Slide 22

Slide 22 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス 暗黙的な実装 ● 構造体(型)がインターフェイスで宣言されたメソッドを 持っていれば、インターフェイスを「暗黙的に」実装し ているとみなされる ● インターフェイスで宣言されたメソッドを持っている

Slide 23

Slide 23 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス 暗黙的な実装 ● 構造体(型)がインターフェイスで宣言されたメソッドを 持っていれば、インターフェイスを「暗黙的に」実装し ているとみなされる ● インターフェイスで宣言されたメソッドを持っている ↓ インターフェイスを満たす

Slide 24

Slide 24 text

GoとPHPのインターフェイス つづいて、Goのインターフェイス 暗黙的な実装 ● 構造体がインターフェイスに必要なメソッドを持ってい れば、インターフェイスを「暗黙的に」実装していると みなされる ● インターフェイスに必要なメソッドを持っている ↓ インターフェイスを満たす インターフェイスを満たす とは???

Slide 25

Slide 25 text

2. Goのインターフェイスを少し深ぼる 1. GoとPHPのインターフェイス 2. Goのインターフェイスを少し深ぼる 3. サンプルを交えてさらに深ぼる 4. まとめ

Slide 26

Slide 26 text

2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング ● Goでは明示的に`implements`を宣言しなくても、必要な メソッドを持っていればインターフェイスを実装してい るとみなされる

Slide 27

Slide 27 text

2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング ● Goでは明示的に`implements`を宣言しなくても、必要な メソッドを持っていればインターフェイスを実装してい るとみなされる ● 「もしそれ(型)が特定のメソッドを持っていて、その動作 が期待通りなら、それはインターフェイスを実装してい るとみなされる」

Slide 28

Slide 28 text

2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング ● Goでは明示的に`implements`を宣言しなくても、必要な メソッドを持っていればインターフェイスを実装してい るとみなされる ● 「もしそれ(型)が特定のメソッドを持っていて、その動作 が期待通りなら、それはインターフェイスを実装してい るとみなされる」 ● 「アヒルのように歩き、アヒルのように鳴くものは、ア ヒルだろう」

Slide 29

Slide 29 text

2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング ● Goでは明示的に`implements`を宣言しなくても、必要な メソッドを持っていればインターフェイスを実装してい るとみなされる ● 「もしそれ(型)が特定のメソッドを持っていて、その動作 が期待通りなら、それはインターフェイスを実装してい るとみなされる」 ● 「アヒルのように歩き、アヒルのように鳴くものは、ア ヒルだろう」→ ダックタイピング

Slide 30

Slide 30 text

2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング 型そのものがインターフェイスを実装してい ることを明示しなくても良い設計を採用して いる ● Go言語プログラミングエッセンス ○ https://www.amazon.co.jp/dp/4297134195 ○ P47, P68 あたり

Slide 31

Slide 31 text

2. Goのインターフェイスを少し深ぼる インターフェイスを満たすとは??? ● インターフェイスには、実装すべきメソッドのシグネ チャ(名前、引数、戻り値)が定義されている ● 構造体(型)がそのインターフェイスに定義されたすべての メソッドを持っている場合、暗黙的にそのインターフェ イスを満たしていると見なされる

Slide 32

Slide 32 text

2. Goのインターフェイスを少し深ぼる インターフェイスを満たすとは??? ● インターフェイスには、実装すべきメソッドのシグネ チャ(名前、引数、戻り値)が定義されている ● 構造体(型)がそのインターフェイスに定義されたすべての メソッドを持っている場合、暗黙的にそのインターフェ イスを満たしていると見なされる ● インターフェイスを満たすとは、インターフェイスに定 義されたすべてのメソッドを提供することを意味する ○ そのままやな

Slide 33

Slide 33 text

3. サンプルを交えてさらに深ぼる 1. GoとPHPのインターフェイス 2. Goのインターフェイスを少し深ぼる 3. サンプルを交えてさらに深ぼる 4. まとめ

Slide 34

Slide 34 text

3. サンプルを交えてさらに深ぼる 構造体がインターフェイスを満たす // Speakerインターフェイスを定義 type Speaker interface { Speak() } // Person構造体を定義 type Person struct{} // PersonにSpeakメソッドを追加 func (p Person) Speak() { fmt.Println("Hello, I am a person.") } func introduce(s Speaker) { s.Speak() } func main() { p := Person{} // Person構造体はSpeakメソッドがあるので、Speakerインターフェイスを満 たす introduce(p) } ● 構造体がインターフェイスを満 たす ○ Person構造体はSpeakerインター フェイスのSpeakメソッドを実装し ているため、暗黙的(自動的)に Speakerインターフェイスを満たす ● インターフェイス型への代入 ○ introduce関数はSpeaker型の引数 を受け取るため、Speakerインター フェイスを満たすPerson構造体を 引数として渡せる

Slide 35

Slide 35 text

3. サンプルを交えてさらに深ぼる カスタム型がインターフェイスを満たす // FormatterInterface インターフェイス type FormatterInterface interface { Format() string } // カスタム型 CustomString を定義 type CustomString string // ❌型エイリアス(型は変わらないのでメソッドを定義できない) // type CustomString = string // Format メソッドを定義(FormatterInterface を満たす) func (s CustomString) Format() string { return strings.ToUpper(string(s)) } func main() { var str FormatterInterface = CustomString("hello, world") fmt.Println(str.Format()) // 出力: HELLO, WORLD } ● カスタム型がインターフェイス を満たす ○ Goは基本型(string, int, bool など) や、複合型(slice, map, chan, function など)に対して型を別で定 義できる ■ カスタム型 ■ type CustomString string ● ※型エイリアスとは違うよ ● type CustomString = string ○ 基本型や複合型を基にしたカスタム 型を作成し、そのカスタム型にメ ソッドを追加することでインター フェースを満たすことができる

Slide 36

Slide 36 text

3. サンプルを交えてさらに深ぼる 利点:振る舞いを切り替えられる type Speaker interface { Speak() } // Dog構造体 type Dog struct{} func (d Dog) Speak() { fmt.Println("I am a dog.") } // Robot構造体 type Robot struct{} func (r Robot) Speak() { fmt.Println("I am a robot.") } func introduce(s Speaker) { s.Speak() } func main() { // Dog, Robot はSpeakerインターフェイスを満たす d := Dog{} r := Robot{} // 振る舞いを切り替えられる introduce(d) // "I am a dog." introduce(r) // "I am a robot." }

Slide 37

Slide 37 text

3. サンプルを交えてさらに深ぼる PHPだとこんな感じ? // Speaker インターフェイス interface Speaker { public function speak(): void; } // Dog クラス class Dog implements Speaker { public function speak(): void { echo "I am a dog." . PHP_EOL; } } // Robot クラス class Robot implements Speaker { public function speak(): void { echo "I am a robot." . PHP_EOL; } } function introduce(Speaker $speaker): void { $speaker->speak(); } // メイン処理 $dog = new Dog(); $robot = new Robot(); introduce($dog); // 出力: I am a dog. introduce($robot); // 出力: I am a robot.

Slide 38

Slide 38 text

3. サンプルを交えてさらに深ぼる 埋め込み // Reader インターフェイス type Reader interface { Read() string } // Writer インターフェイス type Writer interface { Write(data string) } // ReaderとWriterを埋め込んでReadWriterインターフェイスを定義 type ReadWriter interface { Reader Writer } // FileHandler 構造体(ReaderとWriterを実装) type FileHandler struct { content string } func (f *FileHandler) Read() string { return f.content } func (f *FileHandler) Write(data string) { f.content = data } func handleReadWrite(rw ReadWriter) { // データを書き込む rw.Write("Hello, Go!") // データを読み込む fmt.Println("Read data:", rw.Read()) } func main() { file := &FileHandler{} // FileHandlerをReadWriterとして使用 handleReadWrite(file) // "Read data: Hello, Go!" }

Slide 39

Slide 39 text

3. サンプルを交えてさらに深ぼる PHPだとこんな感じ? // Reader インターフェイス interface Reader { public function read(): string; } // Writer インターフェイス interface Writer { public function write(string $data): void; } // ReadWriter インターフェイスは Reader と Writer を継承 interface ReadWriter extends Reader, Writer {} // FileHandler クラス(ReadWriter インターフェイスを実装) class FileHandler implements ReadWriter { private string $content = ''; // Reader インターフェイスの実装 public function read(): string { return $this->content; } // Writer インターフェイスの実装 public function write(string $data): void { $this->content = $data; } } // ReadWriter インターフェイスを引数に取る関数 function handleReadWrite(ReadWriter $rw): void { // データを書き込む $rw->write("Hello, PHP!"); // データを読み込む echo "Read data: " . $rw->read() . PHP_EOL; } // メイン処理 $file = new FileHandler(); // FileHandlerをReadWriterとして使用 handleReadWrite($file); // "Read data: Hello, PHP!"

Slide 40

Slide 40 text

3. サンプルを交えてさらに深ぼる PHPだとこんな感じ? // Reader インターフェイス interface Reader { public function read(): string; } // Writer インターフェイス interface Writer { public function write(string $data): void; } // ReadWriter インターフェイスは Reader と Writer を継承 interface ReadWriter extends Reader, Writer {} // FileHandler クラス(ReadWriter インターフェイスを実装) class FileHandler implements ReadWriter { private string $content = ''; // Reader インターフェイスの実装 public function read(): string { return $this->content; } // Writer インターフェイスの実装 public function write(string $data): void { $this->content = $data; } } // ReadWriter インターフェイスを引数に取る関数 function handleReadWrite(ReadWriter $rw): void { // データを書き込む $rw->write("Hello, PHP!"); // データを読み込む echo "Read data: " . $rw->read() . PHP_EOL; } // メイン処理 $file = new FileHandler(); // FileHandlerをReadWriterとして使用 handleReadWrite($file); // "Read data: Hello, PHP!" ● PHPではextendsを用いてイ ンターフェイスを継承し、新 たなインターフェイスを作成 ● Goの埋め込みは「継承」とい うより「コンポジション(合 成)、委譲」に近い ○ そもそもGoに継承はない

Slide 41

Slide 41 text

3. サンプルを交えてさらに深ぼる 空インターフェイス interface{} (any) func printAnything(v interface{}) { fmt.Println(v) } func main() { printAnything(42) // 整数を出力 printAnything("Hello") // 文字列を出力 printAnything(3.14) // 浮動小数点を出力 printAnything(true) // ブール値を出力 } func main() { data := map[string]interface{}{ "name": "Alice", "age": 25, "active": true, "scores": []int{85, 90, 78}, } // 値を動的に処理 for key, value := range data { fmt.Printf("Key: %s, Type: %T, Value: %v\n", key, value, value) } } Key: name, Type: string, Value: Alice Key: age, Type: int, Value: 25 Key: active, Type: bool, Value: true Key: scores, Type: []int, Value: [85 90 78] ● メソッドを一切持たないイン ターフェイス ● すべての型が暗黙的にこの空イ ンターフェイスを実装している とみなされる

Slide 42

Slide 42 text

3. サンプルを交えてさらに深ぼる 空インターフェイス interface{} (any) func printAnything(v interface{}) { fmt.Println(v) } func main() { printAnything(42) // 整数を出力 printAnything("Hello") // 文字列を出力 printAnything(3.14) // 浮動小数点を出力 printAnything(true) // ブール値を出力 } func main() { data := map[string]interface{}{ "name": "Alice", "age": 25, "active": true, "scores": []int{85, 90, 78}, } // 値を動的に処理 for key, value := range data { fmt.Printf("Key: %s, Type: %T, Value: %v\n", key, value, value) } } Key: name, Type: string, Value: Alice Key: age, Type: int, Value: 25 Key: active, Type: bool, Value: true Key: scores, Type: []int, Value: [85 90 78] PHPだと mixed だな? object|resource|array|string|float|int|bool|null

Slide 43

Slide 43 text

3. サンプルを交えてさらに深ぼる 空インターフェイス 型スイッチ func process(value interface{}) { switch v := value.(type) { case int: fmt.Printf("Integer: %d\n", v) case string: fmt.Printf("String: %s\n", v) case bool: fmt.Printf("Boolean: %t\n", v) default: fmt.Printf("Unknown type: %T\n", v) } } func main() { process(42) // 出力: Integer: 42 process("Go Lang") // 出力: String: Go Lang process(true) // 出力: Boolean: true process(3.14) // 出力: Unknown type: float64 } // 構文 switch v := value.(type) { case T1: // value が T1 型の場合の処理 case T2: // value が T2 型の場合の処理 default: // どの型にも一致しない場合の処理 } // value が実際に保持する具体的な型を判定する // 型スイッチ外では使用できない

Slide 44

Slide 44 text

3. サンプルを交えてさらに深ぼる 空インターフェイス 型アサーション func handle(value interface{}) { if v, ok := value.(int); ok { fmt.Printf("Double the integer: %d\n", v*2) } else if v, ok := value.(string); ok { fmt.Printf("Uppercase the string: %s\n", strings.ToUpper(v)) } else { fmt.Printf("Unhandled type: %T\n", value) } } func main() { handle(10) // 出力: Double the integer: 20 handle("hello") // 出力: Uppercase the string: HELLO handle(3.14) // 出力: Unhandled type: float64 } // 構文 t := i.(T) // インターフェース型の値を特定の型 T に変換(ア サート)する // i が T を保持していない場合、panic を引き起こ す t, ok := i.(T) // インターフェースの値が特定の型を保持している かどうかをテストするために、型アサーションは2 つの値(基になる値とアサーションが成功したかどう かを報告するブール値)を返すことができる // i が T を保持していれば、 t は基になる値にな り、 ok は真(true)になる

Slide 45

Slide 45 text

3. サンプルを交えてさらに深ぼる 空インターフェイスを使う際の注意点 ● 型安全性が低下 ○ 空インターフェイスを多用すると、Goの静的型付けの利点を失 う ● 具体的な型を使える場合は使う ○ 本当に型を柔軟にする必要がある場合のみ空インターフェイスを 使い、可能であれば具体的な型や小さなインターフェイスを設計 する方が望ましい ○ それはそう

Slide 46

Slide 46 text

3. サンプルを交えてさらに深ぼる 空インターフェイスを使う際の注意点 ● 型安全性が低下 ○ 空インターフェイスを多用すると、Goの静的型付けの利点を失 う ● 具体的な型を使える場合は使う ○ 本当に型を柔軟にする必要がある場合のみ空のインターフェイス を使い、可能であれば具体的な型や小さなインターフェイスを設 計する方が望ましい ○ それはそう 避けよう 空インターフェイス あまりメリットはない

Slide 47

Slide 47 text

3. サンプルを交えてさらに深ぼる インターフェイスをさくっと満たせる // TwoMethodsインターフェイスの定義(2つのメソッドが含まれている) type TwoMethods interface { MethodOne() MethodTwo() } // MethodOneのみを持つ(TwoMethodsインターフェイスには適合しない) type PartialOne struct{} func (p PartialOne) MethodOne() {} // MethodOneとMethodTwoを持つ(TwoMethodsインターフェイスに適合) type PartialTwo struct{} func (p PartialTwo) MethodOne() {} func (p PartialTwo) MethodTwo() {} // 3つのメソッドを持つが、TwoMethodsの2つを満たしている type ExtraMethods struct{} func (e ExtraMethods) MethodOne() {} func (e ExtraMethods) MethodTwo() {} func (e ExtraMethods) MethodThree() {} // TwoMethodsインターフェイスを受け取る関数 func useTwoMethods(t TwoMethods) { t.MethodOne() t.MethodTwo() } func main() { partialOne := PartialOne{} partialTwo := PartialTwo{} extraMethods := ExtraMethods{} // エラー: PartialOneはTwoMethodsを満たしていない // useTwoMethods(partialOne) partialOne.MethodOne() useTwoMethods(partialTwo) // 正常に動作する useTwoMethods(extraMethods) // 正常に動作する }

Slide 48

Slide 48 text

3. サンプルを交えてさらに深ぼる インターフェイスをさくっと満たせる // TwoMethodsインターフェイスの定義(2つのメソッドが含まれている) type TwoMethods interface { MethodOne() MethodTwo() } // MethodOneのみを持つ(TwoMethodsインターフェイスには適合しない) type PartialOne struct{} func (p PartialOne) MethodOne() {} // MethodOneとMethodTwoを持つ(TwoMethodsインターフェイスに適合) type PartialTwo struct{} func (p PartialTwo) MethodOne() {} func (p PartialTwo) MethodTwo() {} // 3つのメソッドを持つが、TwoMethodsの2つを満たしている type ExtraMethods struct{} func (e ExtraMethods) MethodOne() {} func (e ExtraMethods) MethodTwo() {} func (e ExtraMethods) MethodThree() {} // TwoMethodsインターフェイスを受け取る関数 func useTwoMethods(t TwoMethods) { t.MethodOne() t.MethodTwo() } func main() { partialOne := PartialOne{} partialTwo := PartialTwo{} extraMethods := ExtraMethods{} // エラー: PartialOneはTwoMethodsを満たしていない // useTwoMethods(partialOne) partialOne.MethodOne() useTwoMethods(partialTwo) // 正常に動作する useTwoMethods(extraMethods) // 正常に動作する } MethodTwo() を実装するだけ

Slide 49

Slide 49 text

3. サンプルを交えてさらに深ぼる インターフェイスをさくっと満たせる // TwoMethodsインターフェイスの定義(2つのメソッドが含まれている) type TwoMethods interface { MethodOne() MethodTwo() } // MethodOneのみを持つ(TwoMethodsインターフェイスには適合しない) type PartialOne struct{} func (p PartialOne) MethodOne() {} // MethodOneとMethodTwoを持つ(TwoMethodsインターフェイスに適合) type PartialTwo struct{} func (p PartialTwo) MethodOne() {} func (p PartialTwo) MethodTwo() {} // 3つのメソッドを持つが、TwoMethodsの2つを満たしている type ExtraMethods struct{} func (e ExtraMethods) MethodOne() {} func (e ExtraMethods) MethodTwo() {} func (e ExtraMethods) MethodThree() {} // TwoMethodsインターフェイスを受け取る関数 func useTwoMethods(t TwoMethods) { t.MethodOne() t.MethodTwo() } func main() { partialOne := PartialOne{} partialTwo := PartialTwo{} extraMethods := ExtraMethods{} // エラー: PartialOneはTwoMethodsを満たしていない // useTwoMethods(partialOne) partialOne.MethodOne() useTwoMethods(partialTwo) // 正常に動作する useTwoMethods(extraMethods) // 正常に動作する } さくっと インターフェイスを 満たせる

Slide 50

Slide 50 text

3. サンプルを交えてさらに深ぼる PHPだとこんな感じ? // TwoMethodsインターフェイスの定義(2つのメソッドを含む) interface TwoMethods { public function methodOne(): void; public function methodTwo(): void; } // methodOneのみを持つ //(TwoMethodsインターフェイスを実装しない, できない) class PartialOne { public function methodOne(): void {} } // methodOneとmethodTwoを持つ(TwoMethodsインターフェイスを実装) class PartialTwo implements TwoMethods { public function methodOne(): void {} public function methodTwo(): void {} } // 3つのメソッドを持つが、TwoMethodsの2つを実装している class ExtraMethods implements TwoMethods { public function methodOne(): void {} public function methodTwo(): void {} public function methodThree(): void {} } // TwoMethodsインターフェイスを受け取る関数 function useTwoMethods(TwoMethods $t): void { $t->methodOne(); $t->methodTwo(); } // メイン処理 function main(): void { $partialOne = new PartialOne(); $partialTwo = new PartialTwo(); $extraMethods = new ExtraMethods(); $partialOne->methodOne(); // エラー: PartialOneはTwoMethodsを実装していない // useTwoMethods($partialOne); useTwoMethods($partialTwo); // 正常に動作する useTwoMethods($extraMethods); // 正常に動作する } main();

Slide 51

Slide 51 text

3. サンプルを交えてさらに深ぼる PHPだとこんな感じ? // TwoMethodsインターフェイスの定義(2つのメソッドを含む) interface TwoMethods { public function methodOne(): void; public function methodTwo(): void; } // methodOneのみを持つ //(TwoMethodsインターフェイスを実装しない, できない) class PartialOne { public function methodOne(): void {} } // methodOneとmethodTwoを持つ(TwoMethodsインターフェイスを実装) class PartialTwo implements TwoMethods { public function methodOne(): void {} public function methodTwo(): void {} } // 3つのメソッドを持つが、TwoMethodsの2つを実装している class ExtraMethods implements TwoMethods { public function methodOne(): void {} public function methodTwo(): void {} public function methodThree(): void {} } // TwoMethodsインターフェイスを受け取る関数 function useTwoMethods(TwoMethods $t): void { $t->methodOne(); $t->methodTwo(); } // メイン処理 function main(): void { $partialOne = new PartialOne(); $partialTwo = new PartialTwo(); $extraMethods = new ExtraMethods(); $partialOne->methodOne(); // エラー: PartialOneはTwoMethodsを実装していない // useTwoMethods($partialOne); useTwoMethods($partialTwo); // 正常に動作する useTwoMethods($extraMethods); // 正常に動作する } main(); methodTwo()を実装して TwoMethodsをimplements する必要がある

Slide 52

Slide 52 text

3. サンプルを交えてさらに深ぼる PHPだとこんな感じ? // TwoMethodsインターフェイスの定義(2つのメソッドを含む) interface TwoMethods { public function methodOne(): void; public function methodTwo(): void; } // methodOneのみを持つ //(TwoMethodsインターフェイスを実装しない, できない) class PartialOne { public function methodOne(): void {} } // methodOneとmethodTwoを持つ(TwoMethodsインターフェイスを実装) class PartialTwo implements TwoMethods { public function methodOne(): void {} public function methodTwo(): void {} } // 3つのメソッドを持つが、TwoMethodsの2つを実装している class ExtraMethods implements TwoMethods { public function methodOne(): void {} public function methodTwo(): void {} public function methodThree(): void {} } // TwoMethodsインターフェイスを受け取る関数 function useTwoMethods(TwoMethods $t): void { $t->methodOne(); $t->methodTwo(); } // メイン処理 function main(): void { $partialOne = new PartialOne(); $partialTwo = new PartialTwo(); $extraMethods = new ExtraMethods(); $partialOne->methodOne(); // エラー: PartialOneはTwoMethodsを実装していない // useTwoMethods($partialOne); useTwoMethods($partialTwo); // 正常に動作する useTwoMethods($extraMethods); // 正常に動作する } main(); TwoMethods を implements して methodTwo() を実装する必要がある なんとなくダルいな? 個人の見解

Slide 53

Slide 53 text

3. サンプルを交えてさらに深ぼる いったんまとめ ● Go ○ ダックタイピングによる暗黙的なインターフェイス実装 ■ さくっと満たせる ○ 柔軟性? ● PHP ○ implements を用いた明示的なインターフェイス実装 ■ 若干めんどい ○ 明確性?

Slide 54

Slide 54 text

3. サンプルを交えてさらに深ぼる インターフェイスをさくっと満たせるが、、 // TwoMethodsインターフェイスの定義(2つのメソッドが含まれている) type TwoMethods interface { MethodOne() MethodTwo() } // MethodOneのみを持つ(TwoMethodsインターフェイスには適合しない) type PartialOne struct{} func (p PartialOne) MethodOne() {} // MethodOneとMethodTwoを持つ(TwoMethodsインターフェイスに適合) type PartialTwo struct{} func (p PartialTwo) MethodOne() {} func (p PartialTwo) MethodTwo() {} // 3つのメソッドを持つが、TwoMethodsの2つを満たしている type ExtraMethods struct{} func (e ExtraMethods) MethodOne() {} func (e ExtraMethods) MethodTwo() {} func (e ExtraMethods) MethodThree() {} // TwoMethodsインターフェイスを受け取る関数 func useTwoMethods(t TwoMethods) { t.MethodOne() t.MethodTwo() } func main() { partialOne := PartialOne{} partialTwo := PartialTwo{} extraMethods := ExtraMethods{} // エラー: PartialOneはTwoMethodsを満たしていない // useTwoMethods(partialOne) partialOne.MethodOne() useTwoMethods(partialTwo) // 正常に動作する useTwoMethods(extraMethods) // 正常に動作する } ここだけを見たときに インターフェイスを 満たしているのかどうか わかりづらい 明示されていない

Slide 55

Slide 55 text

3. サンプルを交えてさらに深ぼる 例えばこういうの user/ ├── domain/ │ └── user.go # ユーザードメイン ├── usecase/ │ └── user_usecase.go # ユースケース ├── infra/ │ └── mysql_user_repo.go # MySQLのリポジトリ実装 └── main.go # エントリーポイント

Slide 56

Slide 56 text

3. サンプルを交えてさらに深ぼる Domain層(user/domain/user.go) package domain type User struct { ID int Name string } // ユーザーデータ操作のインターフェイスを定義 type UserRepositoryInterface interface { GetUserByID(id int) (*User, error) SaveUser(user *User) error }

Slide 57

Slide 57 text

3. サンプルを交えてさらに深ぼる UseCase層(user/usecase/user_usecase.go) package usecase import "user/domain" // ユーザー関連のビジネスロジックを管理 type UserUseCase struct { repo domain.UserRepositoryInterface } // 新しいUserUseCaseインスタンスを作成 func NewUserUseCase(repo domain.UserRepositoryInterface) *UserUseCase { return &UserUseCase{repo: repo} } // 指定されたIDのユーザーを取得 func (u *UserUseCase) GetUser(id int) (*domain.User, error) { return u.repo.GetUserByID(id) }

Slide 58

Slide 58 text

3. サンプルを交えてさらに深ぼる インフラ層(user/infra/mysql_user_repo.go) package infra import ( "fmt" "user/domain" ) // MySQL用のUserRepositoryInterfaceを満たす type MySQLUserRepository struct{} // GetUserByIDはMySQLからユーザーをIDで取得 func (m MySQLUserRepository) GetUserByID(id int) (*domain.User, error) { fmt.Println("MySQLデータベースからユーザーを取得します") return &domain.User{ID: id, Name: "MySQL User"}, nil } // SaveUserはMySQLにユーザーを保存 func (m MySQLUserRepository) SaveUser(user *domain.User) error { fmt.Println("MySQLデータベースにユーザーを保存します") return nil }

Slide 59

Slide 59 text

3. サンプルを交えてさらに深ぼる エントリーポイント(user/main.go) package main import ( "fmt" "user/infra" "user/usecase" ) func main() { // MySQLリポジトリのインスタンスを作成 mysqlRepo := infra.MySQLUserRepository{} // リポジトリをUseCaseに注入 uc := usecase.NewUserUseCase(&mysqlRepo) // ユーザー情報を取得 user, err := uc.GetUser(1) if err != nil { fmt.Println("ユーザー取得中にエラーが発生しました:", err) return } fmt.Printf("ユーザー情報: ID=%d, 名前=%s\n", user.ID, user.Name) }

Slide 60

Slide 60 text

3. サンプルを交えてさらに深ぼる エントリーポイント(user/main.go) package main import ( "fmt" "user/infra" "user/usecase" ) func main() { // MySQLリポジトリのインスタンスを作成 mysqlRepo := infra.MySQLUserRepository{} // リポジトリをUseCaseに注入 uc := usecase.NewUserUseCase(&mysqlRepo) // ユーザー情報を取得 user, err := uc.GetUser(1) if err != nil { fmt.Println("ユーザー取得中にエラーが発生しました:", err) return } fmt.Printf("ユーザー情報: ID=%d, 名前=%s\n", user.ID, user.Name) } LoveUserRepositoryIn terface を注入している

Slide 61

Slide 61 text

3. サンプルを交えてさらに深ぼる インフラ層(user/infra/mysql_user_repo.go) package infra import ( "fmt" "user/domain" ) // MySQL用のUserRepositoryInterfaceを満たす type MySQLUserRepository struct{} // GetUserByIDはMySQLからユーザーをIDで取得 func (m MySQLUserRepository) GetUserByID(id int) (*domain.User, error) { fmt.Println("MySQLデータベースからユーザーを取得します") return &domain.User{ID: id, Name: "MySQL User"}, nil } // SaveUserはMySQLにユーザーを保存 func (m MySQLUserRepository) SaveUser(user *domain.User) error { fmt.Println("MySQLデータベースにユーザーを保存します") return nil } こいつだけを見たときに UserRepositoryInterf ace を満たしているのか どうかわかりづらい 暗黙的に満たされるから

Slide 62

Slide 62 text

3. サンプルを交えてさらに深ぼる インフラ層(user/infra/mysql_user_repo.go) package infra import ( "fmt" "user/domain" ) // MySQL用のUserRepositoryInterfaceを満たす type MySQLUserRepository struct{} // UserRepositoryInterfaceの実装を明示的にチェック var _ domain.UserRepositoryInterface = (*MySQLUserRepository)(nil) // GetUserByIDはMySQLからユーザーをIDで取得 func (m MySQLUserRepository) GetUserByID(id int) (*domain.User, error) { fmt.Println("MySQLデータベースからユーザーを取得します") return &domain.User{ID: id, Name: "MySQL User"}, nil } // SaveUserはMySQLにユーザーを保存 // 〜 こういうチェックも できるが コンパイル時に MySQLUserRepository が UserRepositoryInterface を実装し ているかをチェックする

Slide 63

Slide 63 text

3. サンプルを交えてさらに深ぼる インフラ層(user/infra/mysql_user_repo.go) package infra import ( "fmt" "user/domain" ) // UserRepository実装 type MySQLUserRepository struct{} // UserRepositoryInterfaceの実装を明示的にチェック var _ domain.UserRepositoryInterface = (*MySQLUserRepository)(nil) // GetUserByIDはMySQLからユーザーをIDで取得 func (m MySQLUserRepository) GetUserByID(id int) (*domain.User, error) { fmt.Println("MySQLデータベースからユーザーを取得します") return &domain.User{ID: id, Name: "MySQL User"}, nil } // SaveUserはMySQLにユーザーを保存 // 〜 こういうチェックも できるが 慣れが必要な気がします 個人の見解

Slide 64

Slide 64 text

3. サンプルを交えてさらに深ぼる ちなみにPHPだとこうかな?

Slide 65

Slide 65 text

3. サンプルを交えてさらに深ぼる ちなみにPHPだとこうかな?

Slide 66

Slide 66 text

4. まとめ 1. GoとPHPのインターフェイス 2. Goのインターフェイスを少し深ぼる 3. サンプルを交えてさらに深ぼる 4. まとめ

Slide 67

Slide 67 text

まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装 implements による 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい

Slide 68

Slide 68 text

まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装 implements による 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい implements 明示が不要 ↔ 明示が必要 慣れが必要

Slide 69

Slide 69 text

まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装 implements による 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい https://note.com/ici_mici/n/n4603a3ecd958 「慣れだよ」は最低の助言|ナカミチ

Slide 70

Slide 70 text

まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装 implements を用いた 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい https://note.com/ici_mici/n/n4603a3ecd958 「慣れだよ」は最低の助言|ナカミチ 😇

Slide 71

Slide 71 text

4. まとめ ご清聴ありがとうございました