Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GoとPHPのインターフェイスの違い

shimabox
February 19, 2025

 GoとPHPのインターフェイスの違い

Go Connect #5 での発表資料です。

shimabox

February 19, 2025
Tweet

More Decks by shimabox

Other Decks in Programming

Transcript

  1. 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());
  2. 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()); インターフェイスの定義
  3. 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()); インターフェイスの実装
  4. 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()); インターフェイスに依存
  5. 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注入
  6. 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を実 装しているものであれば 注入できる)
  7. 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{}) }
  8. 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{}) } インターフェイスの定義
  9. 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 構造体
  10. 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は インターフェイスを 満たしている
  11. 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{}) } インターフェイスに依存
  12. 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注入
  13. 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を満 たしているものであれば 注入できる)
  14. 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構造体を 引数として渡せる
  15. 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 ◦ 基本型や複合型を基にしたカスタム 型を作成し、そのカスタム型にメ ソッドを追加することでインター フェースを満たすことができる
  16. 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." }
  17. 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.
  18. 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!" }
  19. 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!"
  20. 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に継承はない
  21. 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] • メソッドを一切持たないイン ターフェイス • すべての型が暗黙的にこの空イ ンターフェイスを実装している とみなされる
  22. 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
  23. 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 が実際に保持する具体的な型を判定する // 型スイッチ外では使用できない
  24. 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)になる
  25. 3. サンプルを交えてさらに深ぼる 空インターフェイスを使う際の注意点 • 型安全性が低下 ◦ 空インターフェイスを多用すると、Goの静的型付けの利点を失 う • 具体的な型を使える場合は使う

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

    ◦ 本当に型を柔軟にする必要がある場合のみ空のインターフェイス を使い、可能であれば具体的な型や小さなインターフェイスを設 計する方が望ましい ◦ それはそう 避けよう 空インターフェイス あまりメリットはない
  27. 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) // 正常に動作する }
  28. 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() を実装するだけ
  29. 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) // 正常に動作する } さくっと インターフェイスを 満たせる
  30. 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();
  31. 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 する必要がある
  32. 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() を実装する必要がある なんとなくダルいな? 個人の見解
  33. 3. サンプルを交えてさらに深ぼる いったんまとめ • Go ◦ ダックタイピングによる暗黙的なインターフェイス実装 ▪ さくっと満たせる ◦

    柔軟性? • PHP ◦ implements を用いた明示的なインターフェイス実装 ▪ 若干めんどい ◦ 明確性?
  34. 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) // 正常に動作する } ここだけを見たときに インターフェイスを 満たしているのかどうか わかりづらい 明示されていない
  35. 3. サンプルを交えてさらに深ぼる 例えばこういうの user/ ├── domain/ │ └── user.go #

    ユーザードメイン ├── usecase/ │ └── user_usecase.go # ユースケース ├── infra/ │ └── mysql_user_repo.go # MySQLのリポジトリ実装 └── main.go # エントリーポイント
  36. 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 }
  37. 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) }
  38. 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 }
  39. 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) }
  40. 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 を注入している
  41. 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 を満たしているのか どうかわかりづらい 暗黙的に満たされるから
  42. 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 を実装し ているかをチェックする
  43. 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にユーザーを保存 // 〜 こういうチェックも できるが 慣れが必要な気がします 個人の見解
  44. 3. サンプルを交えてさらに深ぼる ちなみにPHPだとこうかな? <?php namespace Infra; use Domain\User; use Domain\UserRepositoryInterface;

    class MySQLUserRepository implements UserRepositoryInterface { public function getUserByID(int $id): ?User { echo "MySQLデータベースからユーザーを取得します\n"; return new User($id, "MySQL User"); } public function saveUser(User $user): void { echo "MySQLデータベースにユーザーを保存します\n"; } }
  45. 3. サンプルを交えてさらに深ぼる ちなみにPHPだとこうかな? <?php namespace Infra; use Domain\User; use Domain\UserRepositoryInterface;

    class MySQLUserRepository implements UserRepositoryInterface { public function getUserByID(int $id): ?User { echo "MySQLデータベースからユーザーを取得します\n"; return new User($id, "MySQL User"); } public function saveUser(User $user): void { echo "MySQLデータベースにユーザーを保存します\n"; } } 明示的だね
  46. まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装

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

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

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

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