こんにちは。KOUKIです。
以前、Adapterパターンについて記事を書きましたが、別パターンでも実装したので紹介します。

<目次>
Adapterパターン
Adapterパターンをざっくり説明すると、「既存の機能を変更せずに新しい機能を追加するにはどうすれば良いか?」という課題をクリアするためのものです。
プリンターを例に考えてみましょう。


左が古いプリンターで、右が新しいプリンターだとします。
それぞれ「印刷する」という機能を有していますが、製品自体が違うので当然出入力形式が異なります。
この差異を埋めるために、変換機(Adapter)が必要になるでしょう。
実装
早速、実装してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package adapter import "fmt" // ----------------------------------------------- type LegacyPrinter interface { Print(s string) string } // ----------------------------------------------- type MyLegacyPrinter struct{} func (l *MyLegacyPrinter) Print(s string) string { newMsg := fmt.Sprintf("Legacy Printer: %s\n", s) return newMsg } // ----------------------------------------------- type NewPrinter interface { PrintStored() string } type PrinterAdapter struct { OldPrinter LegacyPrinter Msg string } func (p *PrinterAdapter) PrintStored() string { var newMsg string if p.OldPrinter != nil { newMsg = p.OldPrinter.Print(p.Msg) } else { newMsg = p.Msg } return newMsg } |
LegacyPrinterインターフェースは、古いプリンターの印刷機能を定義したものです。そして、NewPrinterインターフェースは、新しいプリンターの印刷機能を表しています。
そして、今回の肝であるAdapterは、PrinterAdapter構造体で定義しています。この構造体はOldPrinterを保持していることがポイントです。
何故なら、この様に実装すると「古いプリンターの印刷機能が使える」からです。既存の機能を変更することなく、使えると言うことですね。
テストコード
テストコードを実装して、挙動確認をしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package adapter import "testing" func TestAdapter(t *testing.T) { msg := "Hello World!" adapter := PrinterAdapter{ OldPrinter: &MyLegacyPrinter{}, Msg: msg, } returnedMsg := adapter.PrintStored() if returnedMsg != "Legacy Printer: Hello World!\n" { t.Errorf("Message didn't match: %s\n", returnedMsg) } adapter = PrinterAdapter{OldPrinter: nil, Msg: msg} returnedMsg = adapter.PrintStored() if returnedMsg != "Hello World!" { t.Errorf("Message didn't match: %s\n", returnedMsg) } } |
PrinterAdapterで、呼び出すプリンターを選択しています。
そして、古い/新しいにかかわらず「adapter.PrintStored()」で印刷機能を呼び出せました!
テストコードを実行します。
1 2 3 4 5 |
$ go test -v === RUN TestAdapter --- PASS: TestAdapter (0.00s) PASS ok adapter 0.300s |
OKですね。
おわりに
Adapterパターンの使い所はどこなのでしょうか。
将来的にインターフェースが大きくなると予想されるもの?
あるいは、疎結合をを意識したプログラミングを実装する時?
実は、実務では使ったことがないパターンなんですよね。しかし、Adapterを噛ませるという考え方は役にたつと思うので、知識として知っておいてください。
それでは、また!
Go記事まとめ
ソースコード
adapter.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package adapter import "fmt" // ----------------------------------------------- type LegacyPrinter interface { Print(s string) string } // ----------------------------------------------- type MyLegacyPrinter struct{} func (l *MyLegacyPrinter) Print(s string) string { newMsg := fmt.Sprintf("Legacy Printer: %s\n", s) return newMsg } // ----------------------------------------------- type NewPrinter interface { PrintStored() string } type PrinterAdapter struct { OldPrinter LegacyPrinter Msg string } func (p *PrinterAdapter) PrintStored() string { var newMsg string if p.OldPrinter != nil { newMsg = p.OldPrinter.Print(p.Msg) } else { newMsg = p.Msg } return newMsg } |
adapter_test.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package adapter import "testing" func TestAdapter(t *testing.T) { msg := "Hello World!" adapter := PrinterAdapter{ OldPrinter: &MyLegacyPrinter{}, Msg: msg, } returnedMsg := adapter.PrintStored() if returnedMsg != "Legacy Printer: Hello World!\n" { t.Errorf("Message didn't match: %s\n", returnedMsg) } adapter = PrinterAdapter{OldPrinter: nil, Msg: msg} returnedMsg = adapter.PrintStored() if returnedMsg != "Hello World!" { t.Errorf("Message didn't match: %s\n", returnedMsg) } } |
コメントを残す
コメントを投稿するにはログインしてください。