Menu
Coddy logo textTech

デザインパターン入門

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におけるいくつかの基本的なパターンを実装していきます。

challenge icon

チャレンジ

簡単

デザインパターンがコードの整理にどのように役立つかを示す、シンプルな通知システムを構築しましょう!将来のレッスンで 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: メッセージを放送し、各確認を出力する

}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン