Share
Embed
Transcript
71 slides
-
自己紹介 { "NAME": "しまぶ", "SNS": "@shimabox", "TAMASHII": "沖縄", "COMPANY": "カオナビ",
"SKILL": [ "PHP", "<small>Go</small>" ] } whoami.json -
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()); -
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()); インターフェイスの定義 -
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()); インターフェイスの実装 -
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()); インターフェイスに依存 -
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注入 -
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を実 装しているものであれば 注入できる) -
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{}) } -
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{}) } インターフェイスの定義 -
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 構造体 -
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は インターフェイスを 満たしている -
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{}) } インターフェイスに依存 -
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注入 -
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を満 たしているものであれば 注入できる) -
GoとPHPのインターフェイス つづいて、Goのインターフェイス 暗黙的な実装 • 構造体がインターフェイスに必要なメソッドを持ってい れば、インターフェイスを「暗黙的に」実装していると みなされる • インターフェイスに必要なメソッドを持っている ↓
インターフェイスを満たす インターフェイスを満たす とは??? -
2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング • Goでは明示的に`implements`を宣言しなくても、必要な メソッドを持っていればインターフェイスを実装してい るとみなされる • 「もしそれ(型)が特定のメソッドを持っていて、その動作 が期待通りなら、それはインターフェイスを実装してい
るとみなされる」 • 「アヒルのように歩き、アヒルのように鳴くものは、ア ヒルだろう」 -
2. Goのインターフェイスを少し深ぼる Goのインターフェイスはダックタイピング • Goでは明示的に`implements`を宣言しなくても、必要な メソッドを持っていればインターフェイスを実装してい るとみなされる • 「もしそれ(型)が特定のメソッドを持っていて、その動作 が期待通りなら、それはインターフェイスを実装してい
るとみなされる」 • 「アヒルのように歩き、アヒルのように鳴くものは、ア ヒルだろう」→ ダックタイピング -
2. Goのインターフェイスを少し深ぼる インターフェイスを満たすとは??? • インターフェイスには、実装すべきメソッドのシグネ チャ(名前、引数、戻り値)が定義されている • 構造体(型)がそのインターフェイスに定義されたすべての メソッドを持っている場合、暗黙的にそのインターフェ イスを満たしていると見なされる
• インターフェイスを満たすとは、インターフェイスに定 義されたすべてのメソッドを提供することを意味する ◦ そのままやな -
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構造体を 引数として渡せる -
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 ◦ 基本型や複合型を基にしたカスタム 型を作成し、そのカスタム型にメ ソッドを追加することでインター フェースを満たすことができる -
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." } -
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. -
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!" } -
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!" -
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に継承はない -
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] • メソッドを一切持たないイン ターフェイス • すべての型が暗黙的にこの空イ ンターフェイスを実装している とみなされる -
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 -
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 が実際に保持する具体的な型を判定する // 型スイッチ外では使用できない -
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)になる -
3. サンプルを交えてさらに深ぼる 空インターフェイスを使う際の注意点 • 型安全性が低下 ◦ 空インターフェイスを多用すると、Goの静的型付けの利点を失 う • 具体的な型を使える場合は使う
◦ 本当に型を柔軟にする必要がある場合のみ空インターフェイスを 使い、可能であれば具体的な型や小さなインターフェイスを設計 する方が望ましい ◦ それはそう -
3. サンプルを交えてさらに深ぼる 空インターフェイスを使う際の注意点 • 型安全性が低下 ◦ 空インターフェイスを多用すると、Goの静的型付けの利点を失 う • 具体的な型を使える場合は使う
◦ 本当に型を柔軟にする必要がある場合のみ空のインターフェイス を使い、可能であれば具体的な型や小さなインターフェイスを設 計する方が望ましい ◦ それはそう 避けよう 空インターフェイス あまりメリットはない -
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) // 正常に動作する } -
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() を実装するだけ -
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) // 正常に動作する } さくっと インターフェイスを 満たせる -
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(); -
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 する必要がある -
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() を実装する必要がある なんとなくダルいな? 個人の見解 -
3. サンプルを交えてさらに深ぼる いったんまとめ • Go ◦ ダックタイピングによる暗黙的なインターフェイス実装 ▪ さくっと満たせる ◦
柔軟性? • PHP ◦ implements を用いた明示的なインターフェイス実装 ▪ 若干めんどい ◦ 明確性? -
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) // 正常に動作する } ここだけを見たときに インターフェイスを 満たしているのかどうか わかりづらい 明示されていない -
3. サンプルを交えてさらに深ぼる 例えばこういうの user/ ├── domain/ │ └── user.go #
ユーザードメイン ├── usecase/ │ └── user_usecase.go # ユースケース ├── infra/ │ └── mysql_user_repo.go # MySQLのリポジトリ実装 └── main.go # エントリーポイント -
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 } -
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) } -
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 } -
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) } -
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 を注入している -
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 を満たしているのか どうかわかりづらい 暗黙的に満たされるから -
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 を実装し ているかをチェックする -
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にユーザーを保存 // 〜 こういうチェックも できるが 慣れが必要な気がします 個人の見解 -
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"; } } -
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"; } } 明示的だね -
まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装
implements による 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい -
まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装
implements による 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい implements 明示が不要 ↔ 明示が必要 慣れが必要 -
まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装
implements による 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい https://note.com/ici_mici/n/n4603a3ecd958 「慣れだよ」は最低の助言|ナカミチ -
まとめ 4. まとめ Go PHP 実装方法 ダックタイピング による 暗黙的 なインターフェイス実装
implements を用いた 明示的 なインターフェイス実装 メリット 柔軟性が高い、さくっと満たせる 明確で分かりやすい デメリット どこでインターフェイスを実装してい るかが分かりづらい 若干めんどい https://note.com/ici_mici/n/n4603a3ecd958 「慣れだよ」は最低の助言|ナカミチ 😇