デザインパターン入門
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 85/107。
デザインパターンは、ソフトウェア設計において発生する一般的な問題に対する、再利用可能な解決策です。これらは直接コピーするコードではなく、特定の課題を解決するために、型をどのように構造化し、それらがどのように相互作用するかを導くテンプレートです。
デザインパターンは通常、次の3つのカテゴリに分類されます:
| カテゴリ | 目的 | 例 |
|---|---|---|
| 生成(Creational) | オブジェクトの生成メカニズム | Singleton, Factory, Builder |
| 構造(Structural) | 型をより大きな構造に組み立てる | Adapter, Decorator, Composite |
| 振る舞い(Behavioral) | オブジェクト間の通信 | Observer, Strategy, Command |
Goでは、デザインパターンは従来のOOP言語とは異なります。
クラスや継承を使わずに、Goはインターフェース、構造体の埋め込み(struct embedding)、およびコンポジションを使用してパターンを実装します。例えば、Javaが抽象クラスを使用するような場面で、Goはインターフェースを使用します。C++が継承を使用するような場面で、Goは埋め込みを使用します。
パターンを学ぶことは、一般的な問題を認識し、他の開発者と効果的に解決策を共有するのに役立ちます。誰かが「ここではStrategyパターンを使おう」と言えば、長い説明をせずともチーム全体がそのアプローチを理解できます。
これからのレッスンでは、SingletonやFactoryのような生成に関するパターンから始まり、その後、ObserverやStrategyのような振る舞いに関するパターンへと、Goにおけるいくつかの基本的なパターンを実装していきます。
チャレンジ
簡単デザインパターンがコードの整理にどのように役立つかを示す、シンプルな通知システムを構築しましょう!将来のレッスンで Observer や Strategy といったパターンを使って簡単に拡張できるような構造を作成しますが、今は優れた OOP 原則に従ったクリーンなインターフェースと型の設定に集中してください。
コードは 3 つのファイルに分けて整理します:
notifier.go: 通知システムのコアとなるインターフェースと型を定義します。確認メッセージを返す単一のメソッド
Send(message string) stringを持つNotifierインターフェースを作成します。2 つの具体的な通知タイプを実装します:
Addressフィールドを持つEmailNotifier— そのSendメソッドはEmail to [address]: [message]を返します。PhoneNumberフィールドを持つSMSNotifier— そのSendメソッドはSMS to [phone]: [message]を返します。
dispatcher.go: インターフェースを通じて任意の通知機能と連携するディスパッチャーを作成します。Notifierインターフェースのスライスを保持するNotificationDispatcher構造体を構築します。以下のメソッドを追加してください:- 通知機能を登録するための
AddNotifier(n Notifier) - 登録されたすべての通知機能を通じてメッセージを送信し、すべての確認文字列のスライスを返す
Broadcast(message string) []string
また、初期化されたディスパッチャーを返す
NewNotificationDispatcher()コンストラクタも作成してください。- 通知機能を登録するための
main.go: 通知機能を設定し、メッセージをブロードキャストします。作成する通知機能の数を読み取ります。各通知機能について、そのタイプ(
emailまたはsms)と宛先(アドレスまたは電話番号)を読み取ります。それぞれをディスパッチャーに追加します。次に、ブロードキャストするメッセージを読み取ります。すべての通知機能を通じてメッセージを送信し、各確認メッセージをそれぞれの行に出力します。
以下の入力が提供されます:
- 1行目:通知機能の数
- 各通知機能について:タイプ(
emailまたはsms)、次に宛先 - 最終行:ブロードキャストするメッセージ
例えば、以下のように入力された場合:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnight出力は以下のようになります:
Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnightまた、以下のように入力された場合:
2
sms
555-9999
sms
555-0000
Meeting canceled出力は以下のようになります:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledこの構造は、インターフェースがいかに柔軟で拡張可能な設計を可能にするかを示しています。ディスパッチャーは具体的な通知タイプについては何も知らず、ただそれらが Send できることだけを知っています。これが Observer や Strategy といったパターンの基礎となります!
チートシート
デザインパターンは、ソフトウェア設計における一般的な問題に対する再利用可能な解決策です。これらは、型とその相互作用をどのように構造化するかを導くテンプレートです。
デザインパターンは3つのカテゴリに分類されます:
| カテゴリ | 目的 | 例 |
|---|---|---|
| 生成 (Creational) | オブジェクトの生成メカニズム | Singleton, Factory, Builder |
| 構造 (Structural) | 型をより大きな構造に組み立てる | Adapter, Decorator, Composite |
| 振る舞い (Behavioral) | オブジェクト間の通信 | Observer, Strategy, Command |
Goでは、デザインパターンはクラスや継承の代わりに、インターフェース、構造体の埋め込み(struct embedding)、およびコンポジションを使用して実装されます。従来のオブジェクト指向言語が抽象クラスを使用する場面で、Goはインターフェースを使用します。継承を使用する場面で、Goは埋め込みを使用します。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 通知者の数を読み取る
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// 新しいディスパッチャを作成する
dispatcher := NewNotificationDispatcher()
// 各通知者を読み取り、ディスパッチャに追加する
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: タイプに基づいて適切な通知者を作成し、
// ディスパッチャに追加する
// ヒント: notifierType が "email" または "sms" かどうかを確認する
}
// 放送するメッセージを読み取る
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: メッセージを放送し、各確認を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー