LoginSignup
12

デザインパターンの絵本【全23種対応】(文:ChatGPT/画:Midjourney)

Last updated at Posted at 2023-03-30

cover.png

この記事はZennのBookとして公開した同人誌「デザインパターンの絵本」を1記事にまとめたものです。
書籍形式で読みたい方は こちら を参照ください。

はじめに

この本「デザインパターンの絵本」はGoF(Gang of Four)のデザインパターンを絵本を読むように物語ベースで学びたいという思いがきっかけで作り始めました。
これまでに似たようなコンテンツとして「デザインパターンなのに、エモい。」や「ちょうぜつ Advent Calendar 2019」や「Refactoring guruのDesign Pattern」等があり個人的にどちらも非常に好きなコンテンツです。

ただ、全てのデザインパターンを網羅していなかったり、コードスニペットがなかったり/逆に複雑すぎたりして、個人的にはちょうどいいコンテンツがありませんでした。そこに登場したのがChatGPT(GPT-4)とMidjourneyです。この2つのAIツールの力を借りれば、デザインパターンのコンテンツを自動生成できるのではないかと考えました。そうしてこの本が生まれました。

この本の対象読者

  • デザインパターンを学びたい人
  • 一度デザインパターンを学んだんだけどいまいち理解できていない人
  • デザインパターンの雰囲気はわかるけど実際どんなコードを書くかわからない人
  • pythonでのデザインパターンの書き方を知りたい人

前提条件

  • pythonのコードを読むことができる(詳しい内容はわからなくても雰囲気だけつかめれば大丈夫です)

本書の構成

GoFデザインパターン本の3カテゴリ23パターン(後で追加されたパターンについてはまだ対応できていません)それぞれで異なる物語に沿ってデザインパターンを解説します。物語の途中には物語の内容に沿ったpythonコード(詳細な中身は実装されていません)が示され、デザインパターンをどのように実装すればいいかがわかるようになっています。

謝辞

この本は、ChatGPT(GPT-4)とMidjourneyの両名のおかげで完成しました。私mossan-hoshiはその手助けをしたにすぎません。本当にありがとうございます。また、一部の画像では新人のBing Image Creatorにも画像を提供いただきました。感謝いたします。

🚨注意事項🚨

  • 本書はGPT-4を使って自動生成したものです。更に私mossan-hoshiはデザインパターンにそんなに詳しくありません。そのため、文章の内容については保証できません(私自身もこれから精読して学んでいこうと思っている段階です)。もしコードや解説内容に間違いを見つけた場合はお知らせいただけると幸いです。
    • 本書の原稿はGitHubのこちらのリポジトリで公開されてますので、Pull Requestを送っていただけると嬉しいです。
      • もっといい物語や画像を作っていただければ物語ごと差し替えたいと思います。参考までに本書の最後にこの本を作るにあたって使用したChatGPTをのプロンプトを載せていますので、同じように物語や画像を作成できます
    • 細かい表記ブレや誤記に関してはそこまで気にしていませんので優先度は低いです
  • 画像はMidjourneyを使って自動生成したものです。画像の内容は特定の人物や物体を表しているわけではありません。

第1部 生成(Creational)についてのパターン

Creationalデザインパターンは、オブジェクトの作成プロセスを扱うGoFデザインパターンのカテゴリーです。これらのパターンは、インスタンス化プロセスを抽象化し、与えられたユースケースに対してどのオブジェクトを作成する必要があるかを柔軟に決定することができます。作成パターンは、オブジェクトの作成がうまく整理され、将来の変更に容易に適応できるようにすることで、コードの再利用性、保守性、および拡張性を促進します。

1 Factory Method(ファクトリーメソッド) オブジェクトの作成を委ねる

Factory Methodパターンは、スーパークラスでオブジェクトを作成するためのインターフェースを提供する一方、サブクラスで作成されるオブジェクトの種類を変更できるようにする作成デザインパターンです。オブジェクトの生成プロセスをサブクラスに委ねることができ、より柔軟で拡張性の高いコードベースが実現できます。

ファクトリーメソッドパターンは、共通のインターフェイスを持つオブジェクトを作成する必要がある場合に特に有効ですが、実際の実装や作成プロセスが異なる場合があります。ファクトリーメソッドを使用することで、クライアントコードは具体的なクラスの詳細を知る必要がなく、新しいタイプの追加や既存のタイプの変更が容易になります。

2 Abstract Factory(抽象ファクトリー) オブジェクトのファミリーを作成する

抽象ファクトリーパターンは、関連または依存するオブジェクトのファミリーを、その具体的なクラスを指定することなく作成する方法を提供する、もう一つの創造的デザインパターンです。オブジェクトのファミリーを作成するためのメソッド群を定義したインターフェースと、それらのメソッドを実装した1つまたは複数の具体的なファクトリークラスで構成されています。

抽象ファクトリーパターンは、ファミリーに属するオブジェクトを作成する必要があり、それらの作成にはいくつかの共有ロジックやリソースが必要な場合に有効です。このロジックをファクトリークラスにカプセル化することで、コードがよりモジュール化され、オブジェクトの新しいファミリーを追加したり、既存のファミリーを変更したりすることが容易になります。

3 Builder(ビルダー) 構築と表現を切り離す

Builderパターンは、複雑なオブジェクトの構築とその表現を分離することを目的とした創造的デザインパターンです。これは、複雑なオブジェクトを作成するために必要なステップを指定するBuilderインターフェースと、それらのステップを実装する1つまたは複数の具体的なBuilderクラスを導入することによって実現されます。

Builderパターンは、多くのコンポーネントを持つ複雑なオブジェクトを作成する必要がある場合や、作成プロセスが多くのバリエーションを持つ場合に特に有効です。Builderを使用することで、クライアントコードは、構築プロセスの詳細を知らなくても、同じオブジェクトの異なる表現を作成することができます。また、より制御されたステップバイステップの構築が可能になり、コードの保守性や理解も容易になります。

4 Prototype(プロトタイプ) オブジェクトのクローン化

Prototypeパターンは、既存のオブジェクトをコピーまたはクローンすることによって新しいオブジェクトを作成することを扱う創造的なデザインパターンです。Prototypeパターンは、既存のオブジェクトをコピーして新しいオブジェクトを作成する創造的なデザインパターンであり、自身を複製するためのメソッドを定義したPrototypeインターフェースと、このインターフェースを実装した1つまたは複数の具象クラスを導入することによって実現されます。

Prototypeパターンは、オブジェクトの作成にリソースや時間がかかる場合や、同じオブジェクトのインスタンスを多数作成する必要がある場合に有効です。Prototypeを使用することで、クライアントコードは既存のインスタンスをクローンするだけで新しいオブジェクトを作成することができ、ゼロからオブジェクトを作成するコストを避けることができます。また、このパターンでは、クライアントコードに影響を与えることなく、新しいタイプのオブジェクトを追加したり、既存のオブジェクトを変更したりすることが簡単にできます。

5 Singleton(シングルトン) 単一のインスタンスを確保する

Singletonパターンは、クラスが1つのインスタンスしか持たないことを保証し、そのインスタンスへのグローバルなアクセスポイントを提供する創造的なデザインパターンです。クラスのコンストラクタをprivateまたはprotectedにし、そのクラスのユニークなインスタンスを返す静的メソッドを提供することで実現します。

Singletonパターンは、データベース接続、設定オブジェクト、ロギングシステムなど、共有リソースへのアクセスを制御する必要がある場合に有効です。シングルトンを使用することで、クライアントコードはグローバルにアクセス可能な単一のインスタンスを通して共有リソースにアクセスすることができ、重複や競合がないことを保証します。

結論として、Creational Design Patternsはオブジェクトを作成するためのさまざまなテクニックを提供し、より柔軟で保守的、再利用可能なコードを可能にします。これらのパターンを理解し適用することで、開発者は理解、変更、拡張が容易なソフトウェアシステムを構築することができます。

第2部 振る舞い(Behavioral)についてのパターン

ビヘイビアデザインパターンは、オブジェクト間の相互作用を管理するのに役立ち、複雑なシステムの簡素化、メモリ使用の最適化、オブジェクトへのアクセス制御などのソリューションを提供します。これらのパターンを理解し適用することで、より柔軟で保守性の高い、効率的なソフトウェアシステムを実現することができます。

アダプター 互換性のないインターフェイスを接続する

Adapterパターンは、互換性のない2つのインターフェイスを一緒に使えるようにする構造設計パターンです。これは、ターゲットインターフェイスを実装し、被アダプタクラスへの参照を保持するアダプタクラスを導入することによって実現されます。アダプタクラスは、ターゲットインターフェイスからのメソッド呼び出しを、 アダプタクラスの適切なメソッドに変換する。

アダプタパターンは、互換性のないインタフェースを持つコンポーネントを統合する必要がある場合や、コンポーネントのインタフェースを異なるコンテキストに適合させる必要がある場合に有用です。アダプタを使用することで、クライアントコードは何も変更することなく、ターゲットインターフェイスを通してアダプティーと対話することができ、より高い柔軟性と再利用性を可能にします。

ブリッジ 抽象化と実装を切り離す

Bridgeパターンは、抽象化と実装を分離し、両者が独立して進化することを可能にする構造設計パターンである。これは、抽象化を表す抽象クラスと実装を表すインターフェイスを導入することで実現されます。抽象クラスは実装インターフェイスへの参照を保持し、メソッドの呼び出しを委譲する。

Bridgeパターンは、複雑なクラス階層を、抽象化用と実装用の2つの直交する階層に分離する必要がある場合に有効です。Bridgeを使用することで、クライアントコードは実装の詳細を気にすることなく抽象化を使用することができ、より柔軟で保守性の高いコードを実現することができます。

Compositeパターンは、オブジェクトを木構造に構成し、個々のオブジェクトと構成を統一的に扱えるようにする構造設計パターンである。親子関係を管理するメソッドを持つComponentインタフェースと、このインタフェースを実装する1つ以上の具象クラスを導入することで実現する。

Compositeパターンは、部分と全体の階層を表現する必要がある場合や、クライアントコードが個々のオブジェクトとそのコンポジションを一貫した方法で扱う必要がある場合に有用である。Compositeを使用することで、クライアントコードは、個々のオブジェクトとその構成を区別することなく、ツリー構造内のオブジェクトと対話することができ、コードを簡素化し、保守性を向上させることができます。

Decoratorパターンは、オブジェクトの既存の動作に影響を与えることなく、追加の責任で動的に拡張することを可能にする構造設計パターンです。これは、装飾するオブジェクトと同じインタフェースを実装し、そのオブジェクトへの参照を保持するDecoratorクラスを導入することによって実現されます。デコレータークラスは、インターフェースのメソッドをオーバーライドし、その呼び出しをデコレートされたオブジェクトに委譲し、必要に応じて動作を追加・変更します。

Decoratorパターンは、継承を行わずにオブジェクトの機能を拡張する必要がある場合や、オブジェクトに "Decorator "を追加する必要がある場合に有効です。Decoratorを使用することで、クライアントコードはオブジェクトに新しい機能を持たせることができ、その機能は維持されます。を、より柔軟で再利用可能なコードに変更することができます。

ファサード 複雑なサブシステムを簡素化する

Facadeパターンは、複雑なサブシステムに簡素化されたインターフェースを提供する動作設計パターンである。これは、Facadeクラスを導入することで実現され、サブシステムを使いやすくするための上位レベルのインターフェイスとして機能する。Facadeクラスはサブシステムの複雑さをカプセル化し、クライアントコードが単一の簡素化されたインターフェイスを通してサブシステムと対話することを可能にします。

Facadeパターンは、多くのコンポーネントや複雑な依存関係を持つ大規模なシステムやライブラリを扱う場合に、特に有用です。Facadeを使用することで、クライアントコードはサブシステムの複雑性から遮断され、理解や保守が容易になります。さらに、Facadeはサブシステムをクライアントコードから切り離すのに役立ち、システムの柔軟性と再利用性を向上させることができる。

フライウェイト メモリフットプリントを削減する

Flyweightパターンは、複数のインスタンス間で共通部分を共有することで、オブジェクトのメモリ使用量を最小化することを目的とした動作設計パターンである。これは、共有オブジェクトの共通メソッドを定義したFlyweightインターフェイスと、このインターフェイスを実装した1つまたは複数の具体的なFlyweightクラスを導入することによって実現されます。共有オブジェクトはFlyweight Factoryによって管理され、固有のFlyweightのインスタンスが1つだけ作成されるようにします。

Flyweightパターンは、ゲームのグラフィック要素やドキュメントエディターのテキスト文字など、似たような特性を持つオブジェクトを大量に作成する必要がある場合に特に有効です。Flyweightを使用することで、メモリ使用量を大幅に削減することができ、パフォーマンスとスケーラビリティの向上につながる。

Proxyパターンは、別のオブジェクトの代用オブジェクトやプレースホルダーを提供し、元のオブジェクトへのアクセスを制御する動作設計パターンである。これは、元のオブジェクトと同じインターフェイスを持つProxyクラスと、元のオブジェクトへの参照を導入することで実現されます。Proxyクラスは、元のオブジェクトへのすべての呼び出しを傍受し、呼び出しを転送する前または後に追加のアクションを実行できるようにします。

Proxyパターンは、セキュリティチェック、ロギング、キャッシュの提供など、さまざまなシナリオで役立ちます。Proxyを使用することで、クライアントコードは実行される追加アクションを意識することなく元のオブジェクトと対話することができ、よりモジュール化された保守性の高いシステムを実現できます。

第3部 構造(Structural)についてのパターン

構造デザインパターンは、クラスやオブジェクトを構成し、より大きな構造を形成することに重点を置いたGoFデザインパターンのカテゴリです。これらのパターンは、より柔軟で拡張性の高いシステムを作るために、オブジェクトとクラスを組み合わせる効率的な方法を特定することによって、設計を簡素化します。構造パターンは、コンポーネント間の関係がうまく整理され、適切に抽象化されるようにすることで、再利用性、保守性、スケーラビリティを促進します。

chain_of_responsibility(チェーン・オブ・レスポンシビリティ)

Chain of Responsibility(責任の連鎖)とは、オブジェクトがリクエストをハンドラ候補の連鎖に乗せて、いずれかのハンドラが処理するまで渡すことを可能にする動作設計パターンである。このパターンは、リクエストの送信者とその受信者を切り離し、緩やかな結合と責任の分離を促進します。このパターンは、複雑なリクエストや階層的なリクエストを処理する場合に特に有効です。

command(コマンド)

コマンドは、リクエストを、リクエストに関するすべての情報を含む独立したオブジェクトに変換する動作設計パターンです。この変換により、リクエストは保存され、渡され、後で実行されるようになります。Commandパターンは、undo/redo機能、タスクキュー、その他の遅延実行シナリオを実装する際に特に有用です。

interpreter(インタープリター)

インタープリタとは、ドメイン固有の言語や式を解釈する方法を提供する動作設計パターンである。このパターンでは、言語の文法と、その言語の式を評価するインタプリタを定義する。Interpreterパターンは、ドメイン固有言語のパーサー、コンパイラー、インタープリターを実装する際に有用で、特に複雑な文法規則や再帰的な文法規則がある場合に有効です。

iterator(イテレータ)

Iteratorは、基本的な表現を公開することなくコレクションの要素にアクセスする方法を提供する動作設計パターンです。Iteratorパターンは、コレクションの要素を走査するための抽象化を導入し、共通のインターフェイスを使用して異なるコレクションタイプにアクセスすることができます。このパターンは、クライアントコードを簡素化し、懸念事項の分離を促進します。

mediator(Mediator)

Mediatorは、通信の中心点を導入することで、コンポーネント間の結合を低減する動作設計パターンです。Mediatorパターンは、コンポーネント間の多対一の関係を促進し、各コンポーネントはMediatorとのみ通信を行い、Mediatorはコンポーネント間の相互作用を調整する。このパターンは、複雑な相互作用を管理し、疎結合を促進し、コンポーネントの保守性と再利用性を向上させるのに特に有効です。

memento(メメント)

Mementoは、オブジェクトのカプセル化に違反することなく、オブジェクトの内部状態をキャプチャして復元することを可能にする動作設計パターンです。Mementoパターンは、アンドゥ/リドゥ機能の実装や、異なる時点のオブジェクトの状態のスナップショットの作成に特に役立ちます。このパターンでは、状態を保存するために別のMementoオブジェクトを導入し、元のオブジェクトのカプセル化を維持します。

observer(オブザーバー)

Observerは、オブジェクト間の一対多の依存関係を定義する行動デザインパターンであり、あるオブジェクト(主体)の変更が自動的にその依存オブジェクト(オブザーバ)の更新を誘発するようなものである。このパターンは、オブジェクトが共有する主体を介して間接的に通信することを可能にすることで、疎結合を促進します。Observerパターンは、イベント駆動型システムやUIフレームワークでデータバインディングを実装する際によく使用されます。

state(状態)

Stateは、オブジェクトの内部状態が変化したときに、その動作を変化させることを可能にする動作設計パターンである。このパターンでは、可能性のある状態ごとに特定の動作をカプセル化した個別の状態オブジェクトを導入することで、懸念事項の分離を促進し、複雑さを軽減しています。Stateパターンは、有限ステートマシンの実装や、オブジェクトの複雑な状態遷移を管理する場合に特に有効です。

strategy(ストラテジー)

Strategyは、アルゴリズムのファミリーを定義し、それぞれをカプセル化し、互換性を持たせる動作設計パターンです。このパターンにより、クライアントは実行時に適切なアルゴリズムを選択することができ、疎結合と柔軟性を促進することができます。Strategyパターンは、複数のアルゴリズムを互換的に使用できる場合や、アルゴリズムの選択が実行時の条件に依存する場合に特に有効です。

template_method(テンプレート・メソッド)

テンプレートメソッドとは、アルゴリズムの骨格を基底クラスで定義し、一部のステップをサブクラスに委ねる動作設計パターンである。このパターンでは、サブクラスがアルゴリズムの全体構造を変えることなく、特定のステップを再定義することができます。テンプレート・メソッドは、派生クラスでのカスタマイズを可能にしながら、共通の動作を基本クラスに抽出することで、コードの再利用と懸念事項の分離を促進します。

visitor(訪問者)

Visitorは、オブジェクトそのものを変更することなく、既存のオブジェクト構造に新しい操作を追加することを可能にする動作設計パターンです。このパターンでは、新しい操作を実装した別のビジターオブジェクトを導入し、元のオブジェクトはビジターを受け入れて操作を委譲します。ビジターパターンは、異種オブジェクトの集まりで操作を行う場合や、新しい操作を頻繁に追加する必要がある場合に特に有効です。

1_1 Factory Method「宇宙船工場で働くロボットの冒険」

A_group_of_robots_and_human_engineers_work_harmoniously.jpg

新しいロボットワーカー

遠い惑星にある広大な宇宙船工場で、Robo-1という新しいロボット労働者が生産チームに加わりました。その工場は、さまざまな種類の宇宙船を製造していることで有名で、それぞれにユニークな機能と性能を備えていました。ファクトリーメソッドというパターンを使って、Robo-1はさまざまな宇宙船を簡単に作ることができることを知りました。

ファクトリーメソッドパターンを理解する

Robo-1が活動を開始すると、貨物船、旅客船、軍艦など、さまざまな宇宙船モデルがあることが確認されました。これらのモデルを作るために、ファクトリーは共通のインターフェースであるSpaceshipCreatorを使い、create_spaceship()というメソッドを持っています。このインターフェイスにより、ファクトリーは具体的なクラスを指定することなく、さまざまなタイプの宇宙船を作り出すことができるようになりました。

from abc import ABC, abstractmethod

class SpaceshipCreator(ABC):
    @abstractmethod
    def create_spaceship(self):
        pass

Robo-1は、スペースシップのモデルごとに、SpaceshipCreatorインタフェースを実装した独自のクリエータクラスが存在することに気づきました。これらの具体的なクリエータークラスは、特定のスペースシップインスタンスを作成する役割を担っていました。

class CargoShipCreator(SpaceshipCreator):
    def create_spaceship(self):
        return CargoShip()

class PassengerShipCreator(SpaceshipCreator):
    def create_spaceship(self):
        return PassengerShip()

class MilitaryShipCreator(SpaceshipCreator):
    def create_spaceship(self):
        return MilitaryShip()

宇宙船を創る

更にRobo-1は、すべての宇宙船モデルがSpaceshipという共通の基本クラスを共有していることに気づきました。このクラスは、launch()land()のように、すべての宇宙船が使う共通のプロパティとメソッドを定義しています。

class Spaceship(ABC):
    @abstractmethod
    def launch(self):
        pass

    @abstractmethod
    def land(self):
        pass

各スペースシップモデルには、ベースクラスであるSpaceshipを継承した固有のクラスが存在します。これらのクラスは、各スペースシップ・モデルのユニークな機能と動作を実装しています。

class CargoShip(Spaceship):
    def launch(self):
        print("貨物船発進...")
        
    def land(self):
        print("貨物船着艦...")

class PassengerShip(Spaceship):
    def launch(self):
        print("旅客船発進...")
        
    def land(self):
        print("旅客船着艦...")

class MilitaryShip(Spaceship):
    def launch(self):
        print("軍用船発進...")
        
    def land(self):
        print("軍用船着艦...")

まとめ

ファクトリーメソッドパターンを使うことで、Robo-1は具体的なクラスを知らなくても簡単に新しいスペースシップインスタンスを作ることができます。ファクトリーは、特定のクリエーター・クラスのcreate_spaceship()メソッドを呼び出すだけで、新しいスペース・シップのインスタンスを作成できるのです。

1_2 Abstract Factory「魔法のお菓子王国で繰り広げられるお菓子一家の物語」

A_bustling_celebration_scene_in_a_colorful_magical_candy.jpg

はじめに

昔々、魔法のお菓子王国では、有名な菓子職人一家が年に一度のお菓子祭りの準備で忙しくしていました。この一族は、お祭りのために最も精巧でおいしいお菓子を作るのが仕事でした。彼らはAbstractCandyFactoryを使って、ユニークなフレーバーや形をしたさまざまなキャンディーを製造していました。

魔法のようなキャンディ工場

AbstractCandyFactoryは、お菓子の世界ではよく知られたインターフェースで、さまざまなコンクリート工場を実装することで、さまざまなキャンディを作ることができました。最も人気のある2つの具体的な工場は、ChocolateFactoryLollipopFactoryでした。それぞれのコンクリート工場は、特定の種類のお菓子に特化していました。

class AbstractCandyFactory:
    def create_candy(self):
        pass

class ChocolateFactory(AbstractCandyFactory):
    def create_candy(self):
        return Chocolate()

class LollipopFactory(AbstractCandyFactory):
    def create_candy(self):
        return Lollipop()

お菓子の製造工程

菓子職人一家の最年少メンバーであるLily(リリー)は、お菓子の製造工程に魅了されていました。彼女は、AbstractCandyFactoryがコンクリート工場と調和して、最もおいしいお菓子を生産していることに驚嘆しました。

それぞれのファクトリーは、create_candy()というメソッドを持っていて、ファクトリーの専門性に応じてユニークなキャンディーオブジェクトを作り出します。例えば、ChocolateFactoryChocolateオブジェクトを作り、LollipopFactoryLollipopオブジェクトを作ります。これらのキャンディ・オブジェクトは、ベースとなるCandyクラスから派生し、さまざまなキャンディ・タイプの土台となるものでした。

class Candy:
    def __init__(self, flavor, shape):
        self.flavor = flavor
        self.shape = shape

class Chocolate(Candy):
    def __init__(self):
        super().__init__('chocolate', 'bar')

class Lollipop(Candy):
    def __init__(self):
        super().__init__('fruit', 'round')

大切な日がやってくる

キャンディフェスティバルが近づくと、菓子職人一家はAbstractCandyFactoryを使ってたくさんのキャンディを作ることに一生懸命になりました。リリーは勉強熱心で、キャンディの生産を管理する仕事を任されました。

彼女は、作るべきキャンディの種類に応じて適切な具象ファクトリーを選択し、create_candy()メソッドを呼び出すことにしました。こうすることで、家族はそれぞれのキャンディの作成プロセスの複雑な詳細を心配することなく、さまざまな種類のキャンディを効率的に生産することができました。

def create_candy_order(abstract_factory):
    return abstract_factory.create_candy()

lily_chocolate_factory = ChocolateFactory()
lily_lollipop_factory = LollipopFactory()

chocolate = create_candy_order(lily_chocolate_factory)
lollipop = create_candy_order(lily_lollipop_factory)

成功した祝賀会

キャンディ・フェスティバルは大成功を収め、王国中から人々が集まり、菓子職人一家が作った楽しいお菓子を楽しみました。リリーは、AbstractCandyFactoryとコンクリート工場を見事に管理し、キャンディーの生産をスムーズかつ効率的に行い、皆に満足と驚きを与えました。

AbstractCandyFactoryは、ファミリーが王国一のキャンディメーカーとしての名声を維持するために、重要な役割を果たしました。キャンディーの種類から製造プロセスを切り離すことで、変化する顧客の要求や好みに無理なく対応できるようになったのです。

輝く未来

リリーのお菓子作りへの情熱は、Abstract Factoryのデザインパターンの力を目の当たりにして、ますます強くなっていきました。彼女は、このパターンがあれば、家族のビジネスは何世代にもわたってお菓子の世界で繁栄し、革新し続けることができると思ったのです。

そして、お菓子ファミリーとAbstractCandyFactoryは、Abstract Factoryデザインパターンの知恵と汎用性に導かれながら、新しい冒険に乗り出し、新しい課題に直面しながら、魔法のお菓子王国に喜びとおいしさを提供し続けたのです。

1_3 Builder「砂漠のオアシスで家を建てる旅人の挑戦」

A_determined_traveler_stands_in_the_warm_glow_of_the_set.jpg
夕陽に照らされながら、砂漠のオアシスの風景に溶け込む新築のアドビハウスに見入る旅人

1. はじめに

広大な砂漠の真ん中で、疲れ果てた旅人ジャックがたどり着いたのは、緑豊かなオアシスだった。そのオアシスに家を建てることにしたジャックだが、砂漠という特殊な環境のため、さまざまな困難に直面する。そこで彼は、ビルダーパターンを使って、夢の家を建てることにした。

2. ハウスプランの確立

Jackはまず、砂漠の家に必要な必須コンポーネントの概要を示すHousePlanインターフェイスを作成することにした。HousePlanインターフェイスには、buildFoundation()buildWalls()buildRoof()buildWindows()といったメソッドが含まれている。

class HousePlan:
    def buildFoundation(self):
        pass
    def buildWalls(self):
        pass
    def buildRoof(self):
        pass
    def buildWindows(self):
        pass

3. ビルダークラスの作成

そして、Jackは、HousePlanインターフェースを実装したAdobeHouseBuilderBrickHouseBuilderという2つのBuilderクラスを開発した。それぞれのBuilderクラスは、特定のタイプの砂漠の家を建設することに特化している。

class AdobeHouseBuilder(HousePlan):
    # アドビハウスの建設方法の実装

class BrickHouseBuilder(HousePlan):
    # レンガ造りの家を作る方法の実装

4. 住宅を建設する

Builderクラスが揃ったので、Jackはそれを使って砂漠の家を建てることができるようになった。彼はまず、建設プロセスの管理を担当するDirectorクラスのインスタンスを作成した。DirectorクラスはBuilderインスタンスを引数に取り、そのメソッドを用いて特定のタイプに応じた家を建設する。

class Director:
    def __init__(self, builder):
        self.builder = builder

    def construct_house(self):
        self.builder.buildFoundation()
        self.builder.buildWalls()
        self.builder.buildRoof()
        self.builder.buildWindows()
        return self.builder.get_house()

5. 適切なビルダーを選択する

ジャックは砂漠の環境を評価し、高温乾燥の気候に適しているアドビハウスを建てることにした。彼はAdobeHouseBuilderクラスのインスタンスを作成し、ディレクターに渡す。

adobe_builder = AdobeHouseBuilder()
house_director = Director(adobe_builder)

6. ジャックの夢のマイホームを建てる

ジャックは適切なビルダーを選択し、ディレクターを設定したことで、いよいよ砂漠のオアシスに夢の家を建てることができる準備が整った。彼は house_director インスタンスで construct_house() メソッドを呼び出し、AdobeHouseBuilder インスタンスで適切なメソッドを呼び出す。

jack_house = house_director.construct_house()

7. まとめ

JackはBuilderパターンを使って、砂漠のオアシスに夢のマイホームを建てることに成功した。このデザインパターンによって、彼は、建設プロセスにおける柔軟性とモジュール性を維持しながら、砂漠の環境に特化した家を建設することができた。ビルダーパターンの活用により、ジャックは複雑なオブジェクトの建設とその表現を分離することができ、将来的に追加の家を建設することになった場合、異なる家のタイプや材料に対応することが容易になった。

砂漠に日が沈む頃、ジャックは新しいアドビハウスを誇らしげに眺め、満足げに語った。ビルダーパターンのおかげで、彼は砂漠の課題を克服し、オアシスの中心に快適で持続可能な住居を作り上げたのである。

1_4 Prototype「クローンを作る未来都市での個性を見つける探求」

A_vibrant_cityscape_filled_with_diverse_colorful_charact_df.jpg
多様でカラフルなキャラクターが、それぞれの個性を発揮する活気ある街並み。プロトタイプパターンがもたらす新たな創造性と革新性が、彼らの世界を変えています。

クローンの近未来都市

誰もがクローンである未来都市で、市民は個性に憧れていました。その中で、若きアーティスト、アーティは、クローン技術によって誰もが同じような姿や行動をとるようになったにもかかわらず、独自性を表現する方法を見つけ出そうと決意していたのです。

アーティの発見

ある日、アーティは偶然見つけた古い図書館で、Prototypeパターンを発見しました。このデザインパターンは、既存のオブジェクトをコピーして新しいオブジェクトを作ることができ、元のオブジェクトに影響を与えることなく新しいオブジェクトに変更を加えることができることを知りました。

from copy import deepcopy

class Clone:
    def __init__(self, attributes):
        self.attributes = attributes

    def clone(self):
        return deepcopy(self)

このパターンに触発されたアーティは、ユニークな作品を作るためにアートの実験をすることにしました。

ユニークな作品を作る

アーティはまず、Prototypeと呼ばれる基本的な絵画から始めました。そして、Prototypeのパターンを使って、その絵のコピーを作り、それぞれに少しずつ手を加えていきました。色彩を変えたり、新しい要素を加えたり、構図を変えたり、アーティはそれぞれのコピーに異なる変更を加えていきました。

basic_painting = Clone({"colors": ["red", "blue", "yellow"], "composition": "landscape"})

painting1 = basic_painting.clone()
painting1.attributes["colors"] = ["green", "purple", "orange"]

painting2 = basic_painting.clone()
painting2.attributes["composition"] = "abstract"

painting3 = basic_painting.clone()
painting3.attributes["colors"] = ["cyan", "magenta", "yellow"]
painting3.attributes["composition"] = "portrait"

新しい作品のひとつひとつが、アーティの創造性のユニークな表現となり、未来都市の市民は注目しました。

アート革命

アーティのアートが人気を博すにつれ、街の人々も彼の個性的なアプローチに触発されるようになりました。プロトタイプ・パターンを生活のさまざまな場面で活用するようになり、クローンのような単調な生活から脱却していったのです。

ファッションデザイナーは個性的な服を、建築家は個性的な建物を、そしてシェフも斬新な料理を作り始めた。プロトタイプ・パターンは、1つのプロトタイプをもとに、オリジナルに影響を与えることなく、さまざまなユニークなものを作り出すことを可能にしました。

designer_clothing = Clone({"style": "modern", "materials": ["cotton", "silk"]})

unique_dress = designer_clothing.clone()
unique_dress.attributes["style"] = "bohemian"

unique_building = Clone({"architecture": "modern", "materials": ["steel", "glass"]})

green_building = unique_building.clone()
green_building.attributes["materials"] = ["recycled materials", "solar panels"]

変貌する都市

アーティとプロトタイプ・パターンのおかげで、未来都市は活気に満ちた多様な場所に生まれ変わりました。市民は新しい個性と創造性を受け入れ、街はダイナミックでカラフル、そしてエキサイティングな環境となったのです。

## Prototypeパターンを使用してユニークなオブジェクトを作成する 
unique_object1 = Clone({"attribute1": "value1", "attribute2": "value2"})
unique_object2 = unique_object1.clone()
unique_object2.attributes["attribute1"] = "new_value1"

街はイノベーションの中心地となり、クローンという出自にとらわれず、一人ひとりが自分の個性を発揮できるようになりました。人々はもはや自分たちを単なるコピーではなく、自分たちのアイデンティティを持った個人として見ていたのです。

プロトタイプ・パターンの力

アーティの旅と街の変化は、プロトタイプ・パターンの力を証明するものでした。ひとつのプロトタイプをもとにユニークなオブジェを作ることができるデザインパターンは、街の成長、創造性、個人の表現の可能性を引き出していきました。

アーティがアートを作り続け、人々にインスピレーションを与え続けている間、Prototypeパターンは未来都市の市民にとって希望のシンボルであり続けました。クローンの世界でも、個性と才能を発揮できる場があることを教えてくれたのです。

1_5 Singleton「とある村の賢者の栄光と衰退」

In_the_painting_a_wise_figure_stands_atop_a_mountain_ill.jpg
この絵では、一筋の光に照らされた山の上に賢者が立っており、ソフトウェアの世界におけるSingletonパターンのユニークな役割を象徴しています。

はじまり

昔々、ある村に、村人の問題を解決するために重要な役割を果たす賢者が住んでいました。彼は知恵と導きの唯一の源であり、シングルトンパターンを体現する存在でした。シングルトンパターンは、クラスが1つのインスタンスしか持たないことを保証し、それへのグローバルなアクセスポイントを提供します。

class WiseMan:
    _instance = None

    @staticmethod
    def get_instance():
        if WiseMan._instance is None:
            WiseMan._instance = WiseMan()
        return WiseMan._instance

    def __init__(self):
        if WiseMan._instance is not None:
            raise Exception("このクラスはシングルトンです!")
        else:
            WiseMan._instance = self

    def solve_problem(self, problem):
        return "賢者の解決策:" + problem

栄光

その賢者はその知識で有名で、遠く離れた村人たちも彼の助言を求めていた。この場合、シングルトンパターンの強みはそのシンプルさであり、不必要な複雑さを伴わずに誰もが賢者に相談することができるのである。

def main():
    wise_man = WiseMan.get_instance()
    print(wise_man.solve_problem("干ばつ"))

if __name__ == "__main__":
    main()

新たな挑戦

時が経つにつれ、村は大きくなり、賢者の責任は重くなった。そのため、賢者には常に指導の依頼が殺到し、効率的に職務を遂行することが難しくなった。シングルトンパターンの弱点は、増大する要求を効率的に処理するためのスケールアップができないことであった。

衰退

やがて賢者の体調が悪くなり、村人たちは、もはや一人の人間にすべての問題を解決させることはできないと悟った。そこで、村人たちは賢者会議を立ち上げ、責任を分担して解決することにした。

新しいアプローチ

村人たちは、増大するニーズにより適した別のデザインパターン(例:「Strategyパターン」「Chain of Responsibilityパターン」等)を採用することにしました。 彼らは評議会を設立し、複数の賢者が協力して指導できるようにしました。

一般的にはシングルトンに対する改善策としてはDI(依存性注入)等が使われる模様です。詳しくはt_wadaさんによる解説音声(その1その2)をお聴きください

class WiseMan:
    def __init__(self, name):
        self.name = name

    def solve_problem(self, problem):
        return f"{self.name}'s solution to {problem}"

class CouncilOfWiseMen:
    def __init__(self):
        self.wise_men = []

    def add_wise_man(self, wise_man):
self.wise_men.append(wise_man)

def solve_problem(self, problem):
    for wise_man in self.wise_men:
        print(wise_man.solve_problem(problem))
def main():
council = CouncilOfWiseMen()
council.add_wise_man(WiseMan("賢者1"))
council.add_wise_man(WiseMan("賢者2"))
council.add_wise_man(WiseMan("賢者3"))

council.solve_problem("a drought")

if name == "main":
main()

レッスン

村の賢者とシングルトンパターンの話は、デザインパターンは特定の状況では効果的かもしれませんが、状況が変化すると最適な選択ではない可能性があることを教えてくれます。 この場合、村が成長し、賢者への要求が高まるにつれて、シングルトンパターンの限界が明らかになりました。 デザインパターンを実際の問題に適用する際には、デザインパターンのスケーラビリティと適応性を考慮することが不可欠です。

2_1 adapter「アダプターパターンで調和するジャングル」

A_diverse_group_of_animals_gathers_around_a_water_source.jpg
ジャングルの水源に集まる多様な動物たちが、生命力あふれる水を飲み、分かち合うために調和しています。

ジャングルのジレンマ

昔々、うっそうとしたジャングルの中で、さまざまな動物たちが仲良く暮らしていました。動物たちは、それぞれの違いを感じながらも共存し、困ったときには助け合いながら暮らしていました。ある日、ジャングルは干ばつに見舞われ、動物たちは水の分配システムを作ることにしました。

アダプターパターンが窮地を救う

# 既存インターフェース
class Animal:
    def drink(self, water):
        pass

#  新しいインターフェース
class WaterSource:
    def provide_water(self):
        return "water"

# アダプタークラス
class AnimalWaterAdapter(Animal):
    def __init__(self, water_source):
        self.water_source = water_source

    def drink(self, water):
        return self.water_source.provide_water()

# クライアントコード
def water_distribution(animal: Animal, water):
    animal.drink(water)

# 使用方法
source = WaterSource()
adapter = AnimalWaterAdapter(ソース)
water_distribution(adapter, "water")

動物たちは、それぞれの種が独自の方法で水を飲んでいることに気づきました。そこで、それぞれの飲み方を変えずに水を飲める共通のシステムが必要になりました。そこで登場したのが、Adapterパターンです。

Adapterパターンは、互換性のない2つのインターフェースの間のギャップを埋めるのに役立つ構造設計パターンです。ジャングルでは、既存のAnimalクラスにはdrinkメソッドがあり、新しいWaterSourceクラスにはprovide_waterメソッドがありました。動物たちは、既存の飲む動作を変更することなく、水源を利用するためのアダプタを必要としていました。

アダプターの実装

この問題を解決するために、Animalクラスを継承したAnimalWaterAdapterクラスを作成しました。このアダプタは、WaterSourceオブジェクトを引数に取り、drinkメソッドを実装しました。drinkメソッドの内部では、WaterSourceオブジェクトからprovide_waterメソッドを呼び出しています。

すべての動物に水を配る

動物たちは、water_distribution関数を使うことで、動物たち独自の飲み方に関係なく、すべての動物に水を供給できるようになりました。クライアントコードは、水源の根本的な違いや、動物がどのように水を消費するかを意識する必要はありませんでした。アダプタがそれを処理してくれるので、水の分配に集中できるのです。

アダプターのパターンを導入したことで、ジャングルの動物たちは、みんなに効果的に水を配って干ばつを克服することができました。アダプター・パターンが彼らの違いを埋めてくれたおかげで、彼らはより暮らしやすくなり、調和して暮らし続けることができたのです。

まとめ

この物語では、動物たちがこれまでの行動を変えることなく、新しい配水システムを利用できるようにすることで、アダプターのパターンが力を発揮しています。このパターンがあったからこそ、動物たちは違いを感じながらも共存し続け、共に困難を乗り越えていくことができたのです。

2_2 bridge「統一された努力で島の架け橋になる」

A_diverse_group_of_people_from_two_islands_work_together.jpg
2つの島の多様な人々が協力し合い、木と石を組み合わせたユニークな橋を巧みに作りながら、川を渡っていく。

はじめに

かつてひとつだったふたつの島は、危険な川によって分断されてしまった。両島の住民は、再びつながり、関係を再構築することを切望していたが、橋が必要であった。そこで、橋の設計と実装を分離するために、Bridgeパターンを使用することにした。

島をつなぐBridgeパターン

Bridgeパターンを使えば、それぞれの島の資源に応じて、異なる材料や工法で橋を架けることができるようになる。

from abc import ABC, abstractmethod

# アブストラクション ブリッジデザイン
class Bridge(ABC):

    def __init__(self, implementation):
        self.implementation = implementation

    @abstractmethod
    def construct(self):
        pass

# インプリメンテーション 構築技術
class BridgeImplementation(ABC):

    @abstractmethod
    def build(self):
        pass

2つの島とその橋の物語

A島は木材が豊富で、B島は石材が豊富であった。両島の住民は、木材用と石材用の2つの特化した橋の実装を作ることにした。

# 具体的な実装
class WoodenBridge(BridgeImplementation):

    def build(self):
        return "木造の橋"

class StoneBridge(BridgeImplementation):

    def build(self):
        return "石の橋"

橋梁工事におけるコラボレーション

コミュニケーションと協力を促進するために、両島の代表者からなる委員会を設立した。委員会は橋のデザインの抽象化として、全体的な構造と外観を定義する一方、建設の詳細は実装に委ねることになった。

# 洗練された抽象性
class IslandBridge(Bridge):

    def construct(self):
        return f"Island bridge made of {self.implementation.build()}"

# ブリッジの実装を作成する
wooden_bridge = WoodenBridge()
stone_bridge = StoneBridge()

# 実装して島橋を作る
bridge_A = IslandBridge(wooden_bridge)
bridge_B = IslandBridge(stone_bridge)

力を合わせれば、成功する

工事が始まると、2つの島は資源やノウハウを交換し合いながら協力し合った。木の橋はA島から川の真ん中まで、石の橋はB島から真ん中で合流するように建設された。

# 橋を架ける
print(bridge_A.construct()) # 出力されます: 木製の橋でできた島の橋
print(bridge_B.construct()) # 出力されます: 石橋でできた島橋

結論

最終的にBridgeパターンは、両島の資源と技術の粋を集めた橋の建設を可能にし、両島の再会を実現した。橋の設計の抽象化と実装を切り離すことで、2つの島が再びつながり、調和した生活を送るという夢を実現したのだ。

2_3 composite「雪の森のユナイテッドツリー」

A_group_of_trees_interconnected_and_glowing_with_a_myste.jpg

雪景色を背景に、互いにつながり、神秘的なエネルギーを放つ木々が力強く立ち並び、敗れた木こりが背を向ける姿は、結束と協力の力を物語っています。

自然界における複合パターン

雪の降る森では、大きさも種類も違う木々が一緒に暮らしていました。脅威に直面したとき、彼らが一体となって行動できる秘密は、構造設計パターンの一つであるCompositeパターンにありました。Pythonでは、Compositeパターンを使うことで、個々の木や木のグループを一律に扱い、部分と全体の階層を表現することができるようになりました。

from abc import ABC, abstractmethod

class TreeComponent(ABC):
    """
    TreeComponent クラスは、個々のツリーとツリーグループの両方に共通するインターフェイスを定義しています。
    """
    @abstractmethod
    def act(self):
        pass

class Tree(TreeComponent):
    """
    Tree クラスは、TreeComponent インターフェースを実装し、個々の木を表します。
    """
    def __init__(self, tree_type):
        self.tree_type = tree_type

    def act(self):
        return f"{self.tree_type} tree acts."

class TreeGroup(TreeComponent):
    """
    TreeGroup クラスは、TreeComponent インターフェースを実装し、ツリーグループを表します。
    """
    def __init__(self):
        self.trees = []

    def add(self, tree_component):
        self.trees.append(tree_component)

    def remove(self, tree_component):
        self.trees.remove(tree_component)

    def act(self):
        actions = [tree.act() for tree in self.trees]
        return "\n".join(actions)

脅威のアプローチ

ある日、一人の木こりが森にやってきて、商売のために木を切り倒そうとしました。森はこの脅威に驚き、木々は自分たちの住処を守るために早急に行動を起こさなければならないと考えました。

木々は団結する

木はCompositeパターンの力を借りて、木グループを形成して協力することにしました。

# ツリーグループの作成
tree_group_1 = TreeGroup()
tree_group_2 = TreeGroup()
tree_group_3 = TreeGroup()

# ツリーグループに個々のツリーを追加する
tree_group_1.add(Tree("Pine"))
tree_group_1.add(Tree("Oak"))
tree_group_2.add(Tree("Maple"))
tree_group_2.add(Tree("Birch"))
tree_group_3.add(Tree("Spruce"))
tree_group_3.add(Tree("Willow"))

# すべての小さなグループを含む、より大きなツリーグループを作成する。
forest = TreeGroup()
forest.add(tree_group_1)
forest.add(tree_group_2)
forest.add(tree_group_3)

団結の力

そして、木こりの侵入を阻止するために、木々の力を結集し、作戦を立てました。それぞれの木が独自の能力を発揮し、その力を結集することで、侵入者に対抗する強力な力を生み出したのです。

# 森羅万象が一緒に行動する
print(forest.act())

木々が一斉に揺れ、ざわめくと、木こりはますます不安になった。どこからどこまでが木で、どこからどこまでが木なのかがわからなくなり、どの木から切り倒せばいいのかがわからなくなりました。森が一体となり、協調して行動することで、木こりは見事に混乱しました。

木こりの退却

森が一体となった姿に圧倒された木こりは、結局、計画を断念することにしました。木々はコンポジットパターンのおかげで、協力し合って自分たちの家を守ることに成功したのです。

# 個々のツリーグループは独立して行動することも可能です。
print(tree_group_1.act())
print(tree_group_2.act())

この物語からの学び

雪の森の木々はその勝利を喜び、コンポジットパターンを採用し続けました。このパターンによって、彼らは効率的にコミュニケーションをとり、一つの組織として行動することができるようになり、また必要に応じて個々のアイデンティティを維持することができるようになったのです。

この話から、Compositeパターンによって、オブジェクトが部分と全体の階層を形成し、個々のコンポーネントとグループの両方を統一的に扱うことができることがわかります。これは、複雑なシステムや構造を扱う場合に特に有効で、操作を簡略化し、異なる要素間のコラボレーションを促進することができます。

2_4 decorator「クリスマスツリーの飾りが命を宿す不思議な話」

In_a_cozy_living_room_filled_with_the_warmth_of_family_a.jpg
家族と愛の温もりに満ちた居心地の良いリビングルームには、美しく飾られたクリスマスツリーがそびえ立ち、枝には鮮やかなオーナメントが飾られ、魔法の天使の見守る中、柔らかで魅惑的な輝きを放ちながら命を吹き込んでいます。

オーナメントの魔力覚醒

昔々、ある居心地の良い小さな町で、村人たちは1年で最も不思議な時間を過ごすための準備をしていました。クリスマスです!どの家もきらめく光やリースで家を飾り、もちろんオーナメントで飾られた美しいクリスマスツリーも用意しました。

魅惑のオーナメント

ある家では、木の枝に天使のような形をした小さな飾りがぶら下がっていました。この置物は他のものとは違いました。なんと命を吹き込む力を持つ魔法をかけられていたのです!

pythonにデコレーター機能があるので、実際に使う場合は@デコレーターを使うのが一般的です

# ベースとなるコンポーネント
class ChristmasTree:
    def decorate(self):
        return "クリスマスツリーは"

# デコレータクラス
class OrnamentDecorator(ChristmasTree):
    def __init__(self, tree: ChristmasTree):
        self.tree = tree

    def decorate(self):
        return f"{self.tree.decorate()} {self.__class__.__name__},"

class AngelOrnament(OrnamentDecorator):
    pass

tree = ChristmasTree()
tree_with_angel = AngelOrnament(tree)

生命の奇跡

クリスマスイブの夜、天使のオーナメントは突然命を吹き込まれました。天使のオーナメントは、家の中を見回すと、喜びと愛に満ちているのがわかりました。天使は、ツリーの上の他のオーナメントにも幸せをもたらしたいという強い思いを抱きました。

class StarOrnament(OrnamentDecorator):
    pass

class BellOrnament(OrnamentDecorator):
    pass

tree_with_star = StarOrament(tree_with_angel)
tree_with_bell = BellOrnament(tree_with_star)

魔法を広める

天使のオーナメントは、その魔法の力で他のオーナメントに命を吹き込みました。輝く星、鳴り響く鐘、そしてツリーに飾られた他のユニークな装飾品たち。

class SnowflakeOrnament(OrnamentDecorator):
    pass

tree_with_snowflake = SnowflakeOrnament(tree_with_bell)

ア・ジョイフルセレブレーション

それぞれのオーナメントに命が吹き込まれると、彼らは喜び、クリスマスの精神を祝いました。オーナメントたちはキャロルを歌い、物語を語り、自分たちを取り巻く温もりと愛に驚嘆しました。

def display_tree(tree: ChristmasTree):
    print(tree.decorate())

display_tree(tree_with_snowflake)

それぞれの居場所への回帰

夜が更けるにつれ、オーナメントたちは、クリスマスの朝、家族が目覚める前に元の状態に戻らなければならないことに気づきました。そこで、オーナメントたちはお互いに別れを告げ、魔法を使って元の無機質な飾りに戻りました。

思い出に残る

オーナメントは元の状態に戻りましたが、あの特別な夜に共有した魔法は生き続けています。毎年、家族がツリーを飾るとき、オーナメントに命が宿ったあの忘れられないクリスマスイブの温もりと喜びを感じることができたのです。

こうして、魔法のクリスマスツリーのオーナメントの物語は、家族にとって大切な思い出となり、何世代にもわたって語り継がれるようになりました。魔法の天使のオーナメントは、毎年クリスマスイブになると他のオーナメントに命を吹き込み、魔法をかけ続け、愛、喜び、一体感というこの季節の真の精神をみんなに思い出させるのです。

# 魔法は毎年起こる
for year in range(2023, 2030):
    print(f"Year {year}:")
    display_tree(tree_with_snowflake)
    print()

これは、クリスマスツリーのオーナメントに命が宿り、他のオーナメントとそれを大切にする家族に幸せと団結をもたらすという魔法の物語で、Decoratorパターンがどのような役割を果たしたかという話です。PythonのコードとDecoratorパターンを使うことで、物語の中で魔法をかけられた天使のオーナメントのように、ツリーに新しいオーナメントを動的に追加し、そのユニークな効果を体験することができたのです。

2_5 facade「壁の向こうの秘密の世界への入り口を探す子供たちの冒険」

A_group_of_excited_children_stand_in_front_of_a_mysterio.jpg
不思議な壁の前に立つ子どもたちは、目を見開いて驚いています。隠された扉がきらきらと音を立てて開き、その向こうの世界が見えてきます。

謎の壁

むかしむかし、ある小さな村で、子どもたちが、どこまでも続く巨大な壁を見つけました。子どもたちは、その壁の向こうに秘密の世界があるのではないかと興味津々でしたが、その壁はとても頑丈そうで、入り込むことができません。ある日、子どもたちは、隠された入り口があるかどうか、一緒になって探すことにしました。

class MysteriousWall:
    def __init__(self):
        self._hidden_door = HiddenDoor()

    def knock(self):
        self._hidden_door.knock()

    def push(self):
        self._hidden_door.push()

    def pull(self):
        self._hidden_door.pull()

class HiddenDoor:
    def knock(self):
        print("You knocked on the door.")

    def push(self):
        print("You pushed the door.")

    def pull(self):
        print("You pulled the door.")

公開されたファサード

子どもたちの一人、ルーシーは、壁には複雑な模様がいくつもあり、まるで大きな仕掛けの一部であるかのように見えることに気づきました。そこで、「ファサード」をつくって、もっと簡単に壁とインタラクションできるようにしようと提案しました。

class WallFacade:
    def __init__(self, wall: MysteriousWall):
        self._wall = wall

    def interact_with_wall(self):
        self._wall.knock()
        self._wall.push()
        self._wall.pull()

wall = MysteriousWall()
facade = WallFacade(wall)

扉が開く

ルーシーはWallFacadeを使って、壁をノックしたり、押したり、引っ張ったりといろいろな組み合わせを試しました。子どもたちは集まってきて、何かが起こるのを期待しながら待っていました。

このコードだといろいろ工夫している感がないですね。この物語全体を書き直したいと思います🚧🚧

def find_secret_combination(facade: WallFacade):
    facade.interact_with_wall()
    facade.interact_with_wall()
    facade.interact_with_wall()

find_secret_combination(facade)

新しい世界が待っている

何度も試行錯誤の末、ついに秘密の組み合わせが見つかった!壁の向こうには魔法の世界が広がっていたのです。子どもたちは大喜びで、早くこの新しい世界を探検したいと思いました。

ファサードの力

子どもたちは、Facadeパターンが、MysteriousWallのような複雑なシステムとの対話を容易にすることを学びました。シンプルなインターフェースを作ることで、ルーシーとその仲間たちは壁の秘密を解き明かし、まったく新しい冒険の世界を発見することができたのです。

Facadeパターンは、子どもたちにシンプルなインターフェイスを提供しました。そして、複雑なMysteriousWallと対話し、最終的に 秘密の扉を見つけることができるようになりました。

こうして子どもたちは、ファサード柄の力を借りて、壁の向こうの隠された世界で数え切れないほどの冒険をすることになったのです。そして、どんな困難も工夫とチームワークで乗り越えられることを、子どもたちはいつも心に留めていました。

2_6 flyweight「蜂の巣で働くハチたちが効率よく花の蜜を集める物語

A_vibrant_meadow_filled_with_colorful_flowers_as_bees_of.jpg
色とりどりの花が咲き乱れる鮮やかな草原で、さまざまな種類のミツバチが協力して効率よく蜜を集め、繁栄するミツバチの巣の調和に貢献しています。

ビーハイブ(The Beehive)

色とりどりの花が咲き乱れる美しい草原で、木に吊るされた蜂の巣が鳴いていました。蜂の巣は、蜂蜜を作るために花の蜜を集めるために、数え切れないほどの働き者の蜂が住んでいました。蜂の巣を維持するために、効率よく蜜を集める仕組みが必要でした。

ミツバチ工場

class Bee
    def __init__(self, species)
        self.species = species

    def collect_nectar(self, flower)
        print(f"{self.species} ハチが{flower}花から蜜を集めている")

class BeeFactory
    _bees = dict()

    def get_bee(self, species)
        if species not in self._bees
            self._bees[species] = Bee(species)
        return self._bees[species]

bee_factory = BeeFactory()

ミツバチの巣には、さまざまな種類のミツバチを作り出す「ビーファクトリー」という独自のシステムがありました。資源を節約するために、1種類の蜂が1匹しか生まれないようにしていたのです。

ハチミツの収集

flowers = ["バラ", "チューリップ", "ヒマワリ", "ヒナギク"]

for flower in flowers
    bee = bee_factory.get_bee("")
    bee.collect_nectar(flower)

ミツバチは、メアドウにあるさまざまな花から蜜を集める役割を担っていました。ビーファクトリーは、花の種類ごとにミツバチを割り当てました。そして、ミツバチはその花から蜜を集め、蜂の巣に持ち帰るのです。これにより、ミツバチはフライウェイトパターンを使って、ミツバチの種類などの共通属性を共有し、同じ種類のミツバチを複数作らないようにして、効率的に働くことができました。

チームワークとリソースの共有

ミツバチたちは、蜜を集める花の情報を共有し、資源を無駄にしないように協力し合っていました。フライウェイトパターンによって、ミツバチはエネルギーと時間を最適化し、必要な仕事に集中することができました。

for flower in flowers
    bee = bee_factory.get_bee("bumble")
    bee.collect_nectar(flower)

同様に、ビーファクトリーでは、花の種類ごとにマルハナバチを割り当てました。また、蜜を効率よく集めてハチの巣に持ち帰り、コロニー全体の成功に貢献しました。

繁栄する巣箱

ミツバチが熱心に働き、フライウェイトパターンを使用した結果、ミツバチの巣は繁栄しました。ミツバチは豊富なハチミツを生産し、コロニーはより強く成長し続けました。草原は、ミツバチたちの調和のとれた協力のおかげで、生き生きとした色彩を保っていました。

この物語で、フライウェイト・パターンは、効率的で資源を節約した方法でミツバチを作り、管理するビーファクトリーの能力で表現されています。同じ種類の蜂を再利用することで、蜂の巣は活力を保ち、コロニーの貴重なエネルギーと時間を、蜜の収集や蜂蜜の生産といった必要な仕事に向けることができます。

2_7 proxy「おとぎ話の世界で他のキャラクターに化ける魔法使いの冒険」

a_diverse_group_of_charact.jpg
騎士、王女、農夫など、さまざまなキャラクターに変身した魔法使いが、魔法のオーラに包まれて佇んでいます。

第1章 魔法的任務

おとぎ話の世界で、賢い魔法使いのエルドリンは、妖精の女王から魔法のような任務を受けました。その仕事とは、さまざまなキャラクターの姿になり、障害を乗り越え、目的を達成するための手助けをすることだったのです。

class Character(ABC):
    @abstractmethod
    def perform_task(self):
        pass

class Wizard(Character):
    def __init__(self, name):
        self.name = name

    def perform_task(self):
        print(f"{self.name}ウィザードが魔法のタスクを実行しています")

第2章 プロキシウィザード

エルドリンはこの任務を遂行するために、自分が代理人となり、助けようとする人物の姿になることができる呪文を開発しました。この呪文によって、彼は自分のアイデンティティを維持しながら、他の人には困っているキャラクターとして見えるようになりました。

class ProxyWizard(Character):
    def __init__(self, wizard: Wizard, character_type: str):
        self.wizard = wizard
        self.character_type = character_type

    def perform_task(self):
        print(f"{self.wizard.name}ウィザードが {self.character_type} に変身した")
        self.wizard.perform_task()

第3章 ナイトに変身する

エルドリンが最初に変身したのは、恐ろしいドラゴンを倒す必要のある勇敢な騎士でした。プロキシの呪文を使い、エルドリンは騎士の姿になってドラゴンに立ち向かい、最終的にドラゴンを倒して王国を救いました。

# 魔法使いと騎士の代理人を作る
eldrin = Wizard("Eldrin")
knight_proxy = ProxyWizard(eldrin, "knight")

# エルドリンは騎士に変身して任務を遂行する
knight_proxy.perform_task()

第4章 プリンセスになる

次にエルドリンは、悪い魔女が守る塔から逃げなければならないお姫様に変身しました。代理の呪文で姫の姿になり、魔女を出し抜いて無事に王国に帰ってきました。

# プリンセスプロキシーを作成する
princess_proxy = ProxyWizard(eldrin, "princess")

# エルドリンは姫に変身して任務を遂行する
princess_proxy.perform_task()

第5章 最後の変身

エルドリンは最後の仕事として、飢えた王国を養うために魔法の作物を栽培する必要がある謙虚な農夫に変身しました。プロキシスペルで農夫になった彼は、魔法の知識を使って作物を栽培し、人々を飢えから救いました。

# 農家のプロキシを作る
farmer_proxy = ProxyWizard(eldrin, "farmer")

# エルドリンは農夫に変身して任務を遂行する
farmer_proxy.perform_task()

第6章 報われた報い

すべての任務を終えたエルドリンは、妖精の女王のもとに戻り、その優れた変身能力を褒められました。女王はエルドリンの協力に感謝し、特別な報酬として、代理人なしで自由にどんなキャラクターにも変身できる能力を与えました。

エルドリンは、この新しい力を使って、おとぎ話の世界の住民を助け、平和と繁栄を約束し続けました。

3_1 chain_of_responsibility「ドミノ倒しで繋がる小さな動物たちの協力物語」

Disney_Pixar_Style_A_squirrel_bird_and_rabbit_work_toget.jpg
リス、鳥、ウサギが息の合ったコンビネーションで、ドミノの列を組んでバリアを作り、その背景には鮮やかな森が広がっています。

はじめに

森の中にある小さな村で、ある動物たちがユニークな挑戦をしました。ドミノ倒しの要領で、村に洪水を起こさないようにするのです。動物たちは、それぞれの特技を生かして協力し合いながら、この課題をクリアしていきました。

責任の連鎖パターン

class Animal:
    def __init__(self, successor=None):
        self.successor = successor

    def set_successor(self, successor):
        self.successor = successor

    def handle_request(self, request):
        if self.successor:
            self.successor.handle_request(request)

Animalクラス

Animalクラスは、責任の連鎖パターンを表し、各アニマルは固有の役割と後継者を持ちます。ある動物がタスクを完了できない場合、そのリクエストを次の動物に渡します。

class Squirrel(Animal):
    def handle_request(self, request):
        if request == "gather":
            print("Squirrel: I'll gather the dominoes!")
        else:
            super().handle_request(request)

class Bird(Animal):
    def handle_request(self, request):
        if request == "position":
            print("Bird: I'll position the dominoes!")
        else:
            super().handle_request(request)

class Rabbit(Animal):
    def handle_request(self, request):
        if request == "push":
            print("Rabbit: I'll push the dominoes!")
        else:
            super().handle_request(request)

チェーンの実装

SquirrelBirdRabbitクラスは、タスクを完了させるために動物が持つ固有の役割を表しています。それぞれの動物が処理できるタスクは決まっており、リクエストを処理できない場合は、後継者に渡します。

squirrel = Squirrel()
bird = Bird()
rabbit = Rabbit()

squirrel.set_successor(bird)
bird.set_successor(rabbit)

チェインの実行

リス(squirrel)がきっかけとなり、動物たちが連鎖的につながっていきます。

squirrel.handle_request("gather")
squirrel.handle_request("position")
squirrel.handle_request("push")

タスクを完了させる

森の中心で、動物たちは力を合わせてこの難題を乗り越えました。リスはドミノを集め、鳥はドミノを配置し、ウサギはドミノを押して、洪水から家を守る障壁を作り上げました。

まとめ

動物たちのチームワークは、責任の連鎖パターンに象徴されるように、協力し合い、独自のスキルを駆使することで、どんな困難も乗り越えられることを示したのです。

3_2 command「時間を操る時計を使って歴史を守るタイムトラベラーの物語」

A_time_traveler_stands_beside_an_ancient_clock_its_hands.jpg
タイムトラベラーが古時計のそばに立ち、その針が時を超えて渦を巻きながら、歴史的な出来事の渦を巻き起こします。

タイムトラベラーと時計

時間が流動的な世界で、勇敢なタイムトラベラー、アレックスは、歴史を操作しようとする者たちから歴史を守るために旅に出ます。神秘的な時計の助けを借りて、アレックスは時間を操作するコマンドを発行することができ、柔軟性と制御を確保するためにコマンドパターンの力を使用します。

# コマンドインターフェース
class TimeCommand:
    def execute(self):
        pass

# 具体的なコマンド
class RewindTimeCommand(TimeCommand):
    def __init__(self, clock):
        self.clock = clock

    def execute(self):
        self.clock.rewind_time()

class FastForwardTimeCommand(TimeCommand):
    def __init__(self, clock):
        self.clock = clock

    def execute(self):
        self.clock.fast_forward_time()

時計とその力

神秘の時計はインボーカーとして、様々な時間コマンドを記憶し実行することができます。これにより、アレックスは複雑な時間操作のタスクを簡単に実行することができました。

# Invoker
class TimeManipulatorClock:
    def __init__(self):
        self.command = None

    def set_command(self, command: TimeCommand):
        self.command = command

    def manipulate_time(self):
        if self.command:
            self.command.execute()

# Receiver
class Clock:
    def rewind_time(self):
        print("時間が巻き戻される...")

    def fast_forward_time(self):
        print("時間が早送りされています...")

時間を操作して履歴を保存する

アレックスは時を越えて、時間を操作しなければならない様々な状況に遭遇します。ある例では、古代の芸術品の破壊を防ぐために時間を巻き戻す必要がありました。

clock = Clock()
time_manipulator = TimeManipulatorClock()

rewind_time = RewindTimeCommand(clock)
time_manipulator.set_command(rewind_time)
time_manipulator.manipulate_time()

別のシナリオでは、アレックスは時間を早送りして、ある出来事の結果を目撃し、それを変更すべきかどうかを判断する必要があります。

fast_forward_time = FastForwardTimeCommand(clock)
time_manipulator.set_command(fast_forward_time)
time_manipulator.manipulate_time()

歴史を守る

コマンドパターンを利用することで、アレックスは神秘的な時計を使って効率的に時間を操作することができます。このパターンでは、既存のコードを変更することなく、新しい時間操作コマンドを追加することができ、タイムトラベラーは歴史を守るためのクエストを続けることができます。アレックスと彼らの時計の物語は、コマンドパターンのパワーと柔軟性を証明するものとなっていきます。

3_3 interpreter「言語を操る魔法使いが世界の謎を解き明かす冒険」

A_young_wizard_surrounded_by_ancient_books_and_scrolls_s.jpg
古代の書物や巻物に囲まれた若い魔法使いが、謎の石版を研究し、魔法を使ってシンボルを解読し、隠された知識を明らかにしていきます。

魔法の言葉

魔法生物と未踏の地に満ちた世界で、若き魔法使いエルウィンは、言語を操る隠れた才能を発見します。その才能は、古文書を解読し、魔法生物の言葉を理解することを可能にしました。エルウィンの才能は、世界の謎を解き明かすために不可欠な道具となっていきます。

インタープリターパターンの動作

魔法言語の研究を深めるエルウィンは、「インタープリター・パターン」を記した書物に出会います。文法規則を表現し、その規則で文章を解釈する方法であることを知ります。そして、インタープリターパターンを使えば、複雑な魔法言語も効率よく解読・理解できることを知ります。

from abc import ABC, abstractmethod

class Expression(ABC):
    @abstractmethod
    def interpret(self, context):
        pass

class TerminalExpression(Expression):
    def __init__(self, value):
        self.value = value

    def interpret(self, context):
        return self.value in context

class OrExpression(Expression):
    def __init__(self, expr1, expr2):
        self.expr1 = expr1
        self.expr2 = expr2

    def interpret(self, context):
        return self.expr1.interpret(context) or self.expr2.interpret(context)

class AndExpression(Expression):
    def __init__(self, expr1, expr2):
        self.expr1 = expr1
        self.expr2 = expr2

    def interpret(self, context):
        return self.expr1.interpret(context) and self.expr2.interpret(context)

魔法のような出会い

ある日、エルウィンは、未知の魔法言語と思われる記号が刻まれた不思議な石版を見つけました。彼は、この石版を解読するために、インタープリターパターンを適用することを決意します。

# 魔法言語を解釈するための表現を作る
def get_magic_language_expression():
    # 魔法言語ごとに TerminalExpression を作成する
    language1 = TerminalExpression("エレメンタム")
    language2 = TerminalExpression("アルカナム")
    # 2 つの TerminalExpressions の間の
    # ジャンクションとして機能する OrExpression を返す
    return OrExpression(language1, language2)

magic_language = get_magic_language_expression()

エルウィンはInterpreterパターンを使って式木を構築し、魔法言語を簡単に解釈することができるようになりました。

# 魔法の言葉を読み解く
context = "エレメンタム, イグニス, アクア ,テラ ,アルカナム ,ヴェントゥス"
result = magic_language.interpret(context)

解き明かされた秘密

インタープリターパターンのおかげで、エルウィンは石版のメッセージを読み解くことに成功します。すると、そこには魔法の力を増幅させる強力なアーティファクトが隠された宝の在処が記されていた。エルウィンは、言語操作とインタープリターパターンを駆使して、宝物を取り戻す旅に出ます。

エルウィンは世界を探索し、謎を解き明かしながら、インタープリターパターンを使いこなしていきます。新しい言語に出会うたびに、エルウィンは世界の魔法をより深く理解し、言語の力をより深く理解するようになるのです。

3_4 iterator「珍しい植物を探すため、園芸家が庭園を巡る物語」

A_horticulturist_carefully_navigates_through_a_lush_gard.jpg
園芸家が、希少種への道を照らす魔法の本に導かれながら、鮮やかで多様な植物が生い茂る緑豊かな庭園を慎重に進んでいきます。

はじめに

鮮やかな色彩とユニークな植物種に満ちた美しく多様な世界で、情熱的な園芸家の主人公は、国中の庭園に隠された希少な植物を発見する旅に出ます。

旅のはじまり

園芸家は、珍しい植物を集めるために、さまざまな庭園を探索する旅に出ます。無数の植物が存在する庭園の中で、彼らは魔法の本を使い、順を追って進んでいきます。この本は、園芸家が各植物に順次アクセスできるようにするイテレータの役割を果たします。

class GardenIterator:
    def __init__(self, plants):
        self._plants = plants
        self._current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self._current >= len(self._plants):
            raise StopIteration
        plant = self._plants[self._current]
        self._current += 1
        return plant

マジカルガーデンズ

それぞれの庭には個性的な植物があり、園芸家はその秘密を解き明かすためにイテレーターを頼りにしています。イテレータは、各庭園をシームレスに横断し、見落としのないようにすることができます。

class Garden:
    def __init__(self, plants):
        self._plants = plants

    def __iter__(self):
        return GardenIterator(self._plants)

希少な植物が明らかに

園芸家が庭園を散策しながら、イテレーターを使って希少価値の高い植物を発掘していきます。魔法の森、神秘的な草原、隠された木立の中を旅していきます。イテレーターの助けを借りて、彼らは行く手にあるユニークな植物をすべて見つけ、記録することができるようになりました。

# 使用例
garden = Garden(["Rose", "Lily", "Orchid", "Lotus", "Iris"])
for plant in garden:
    print(plant)

最後の発見

最後の庭で、園芸家は最も希少な植物、伝説の「ゴールデンブロッサム」を発見します。その植物とは、伝説の「金の花」でした。感動と感謝の気持ちを込めて、園芸家は「金の花」を大切に採取し、冒険を終えました。

おわりに

Iteratorパターンの力で、園芸家は広大で多様な植物の世界を見事にナビゲートし、希少な種を発見し、庭園の謎を解き明かします。この物語では、Iteratorパターンがいかに複雑なトラバースを簡略化し、コレクション内の要素にシームレスにアクセスできるかを示しています。

3_5 mediator「対立する部族が仲介者の力で和解を目指す物語」

A_wise_mediator_stands_between_the_leaders_of_two_once-w.jpg
かつて対立していた2つの部族の指導者の間に立ち、両手を広げ、青空の下で握手を交わし、コミュニケーションによってもたらされた新たな平和と協力の証とする。

戦国時代の部族

昔々、紛争が絶えない土地で、モンテン族とリベリオン族という2つの部族が戦争をしていた。同じ地域に住んでいながら、共有する資源をどのように管理するかで意見がまとまらなかったのだ。両者の間には敵意が芽生え、混乱と不信を招いていた。

class Montain:
    def send_message(self, message: str):
        pass

class Riverian:
    def send_message(self, message: str):
        pass

賢明な調停者

最も困難な土地でさえも平和と調和をもたらすことで知られる賢明な調停者(Mediator)が、モンテンとリベリアの対立を耳にした。彼女は介入して、彼らが解決策を見つけるのを助けることにした。

    def __init__(self, montain: Montain, riverian: Riverian):
        self.montain = montain
        self.riverian = riverian

    def relay_message(self, sender, message: str):
        if sender == self.montain:
            self.riverian.send_message(message)
        else:
            self.montain.send_message(message)

架け橋になる

調停者は両部族の指導者に会い、彼らの不満や懸念に耳を傾けた。調停者は、両部族の指導者に会い、不満や懸念に耳を傾け、悪意なく公平にメッセージを伝えるよう、自分を通してコミュニケーションをとるよう促した。そして、両部族の架け橋となることで、両部族が共通の目標を多く持っていることを認識させたのだ。

class Montain:
    def __init__(self, mediator: Mediator):
        self.mediator = mediator

    def send_message(self, message: str):
        self.mediator.relay_message(self, message)

class Riverian:
    def __init__(self, mediator: Mediator):
        self.mediator = mediator

    def send_message(self, message: str):
        self.mediator.relay_message(self, message)

新たなはじまり

調停者のたゆまぬ努力により、モンテンとリベラルは共通点を見出すようになった。両者は協力することの利点を理解し、相違点は克服できないものではないことを理解した。そして、調停人の助けを借りて、両者が共有する資源や懸念事項に対応した新しい協定を作り上げた。

montain = Montain()
riverian = Riverian()
mediator = Mediator(montain, riverian)

montain.mediator = mediator
riverian.mediator = mediator

montain.send_message("資源の公平な分配を提案します。")
riverian.send_message("我々は同意する、両部族の繁栄のために協力しよう。")

平和と繁栄

調停者の助けにより、モンテンとリベリアの人々は対立を過去のものとした。その結果、モンテンとリヴァーリアンは協力し合い、豊かで調和のとれた国土を作り上げた。

調停者の知恵と献身的な努力によって、一度は乱れた国に平和がもたらされ、紛争を解決し、協力を促す調停者のパターンの重要性が示された。

3_6 memento「失われた記憶を取り戻すためのタイムカプセルを発見する友達の物語」

A_group_of_friends_surrounded_by_a_warm_glow_gather_arou.jpg
温かい光に包まれた友人たちが、開いたタイムカプセルを囲み、喜びと懐かしさに満ちた表情で、共通の過去から大切な思い出の品々を手にしています。

タイムカプセルの発見

ある絵のように美しい小さな町で、大人になった幼なじみたちが、何年も前に埋めたまま忘れていたタイムカプセルを再発見するために再会を果たしました。彼らの望みは、共有する過去のエッセンスを捉えた思い出の品を取り戻すことでした。

# オブジェクトの状態を保存するMementoクラス
class Memento:
    def __init__(self, state):
        self._state = state

    def get_state(self):
        return self._state

思い出を掘り起こす

カプセルの正確な位置はわからりませんでしたが、目印になるものをいくつか覚えていました。探しながら、彼らはさまざまな物や手がかりに出くわし、幼い頃の冒険の思い出がよみがえりました。

# メメントオブジェクトの作成と復元を担当するオリジネータークラス
class TimeCapsule:
    def __init__(self, state):
        self._state = state

    def create_memento(self):
        return Memento(self._state)

    def restore_memento(self, memento):
        self._state = memento.get_state()

ジャーニー・スルー・タイム

思い出の品を見つけるたびに、友人たちはまるでタイムスリップしたかのような感覚に陥りました。手紙や絵、小物など、思い出の品々を見つけるたびに、さまざまな感情や過去のエピソードが溢れ出てきました。思い出の品が発掘されるにつれ、彼らは一体感を感じ、自分たちの絆に感謝するようになりました。

Copy code
# メメントスを管理する「ケアテーカー」クラス
class MemoryKeeper:
    def __init__(self):
        self._mementos = []

    def add_memento(self, memento):
        self._mementos.append(memento)

    def get_memento(self, index):
        return self._mementos[index]

思い出の品に込められた力

ようやく見つけたタイムカプセルを丁寧に開けると、中には大切な思い出の品が入っていました。友人たちはそれぞれ順番に記念品を提示し、それにまつわる思い出を回想し、過去を保存するための記念品のパターンの力を感じていました。

Copy code
# クライアントコード
def main():
    memory_keeper = MemoryKeeper()
    time_capsule = TimeCapsule("Childhood memories")

    memory_keeper.add_memento(time_capsule.create_memento())
    time_capsule._state = "Present time"

    saved_memory = memory_keeper.get_memento(0)
    time_capsule.restore_memento(saved_memory)
    print(time_capsule._state)  # Output: Childhood memories

main()

絆が深まる

その日が終わると、友人たちはタイムカプセルの本当の価値と思い出に気づきました。タイムカプセルは、自分たちの人生の一部を保存・復元するものであり、その結果、二人の距離を縮めることになりました。思い出を共有し、追体験することで、彼らは絆を大切にし、これからもずっとつながって思い出を作っていこうと誓ったのです。

3_7 observer「天文台で働く若者が星の動きによるサインを解読する物語」

A_young_astronomer_gazes_at_a_starry_sky_through_a_teles.jpg
若い天文学者が古い天文台の上にある望遠鏡で星空を眺め、その背後では村人が天のお告げのもと、さまざまな行事の準備を進めている。

はじめに 天文台と若き天文家たち

ある人里離れた小さな村に、古い天文台で星の動きを観察し、解読するイーサンという青年がいました。彼は天文学に深い情熱を持ち、星には村の未来を変える秘密が隠されていると信じていました。

オブザーバー・パターンの発見

星を研究しているうちに、イーサンは星の動きを追跡し、解釈するためのもっと効率的な方法が必要だと気づきました。彼は、星と村のさまざまな出来事の間に関係を確立することができるオブザーバーパターンに出くわしたのです。

from abc import ABC, abstractmethod


class StarObserver(ABC):
    @abstractmethod
    def update(self, star_event):
        pass

Ethanは、天体の動きを見て解釈するさまざまなオブザーバーの設計図となる、抽象的なStarObserverクラスを作成しました。

オブザーバーを実装する

イーサンは、3つの主要なイベントを確認した: スターアライメント、コメットサイティング、ルナーエクリプスです。彼は、効率的に解釈するために、それぞれのイベントに対して別々のオブザーバーを作成することにしました。

class StarAlignmentObserver(StarObserver):
    def update(self, star_event):
        if star_event == "StarAlignment":
            print("繁栄と成長を象徴する星の配置を観察")

class CometSightingObserver(StarObserver):
    def update(self, star_event):
        if star_event == "CometSighting":
            print("彗星を目撃、重要な訪問者の到来を示す")

class LunarEclipseObserver(StarObserver):
    def update(self, star_event):
        if star_event == "LunarEclipse":
            print("月食を観測し、今後の課題を警告する")

各オブザーバーは、それぞれの天体イベントを処理するための特定のメソッドを持っていました。

星を繋ぐ

Ethanは、イベントが発生したときにオブザーバーに通知する方法が必要であることに気づきました。彼は、オブザーバーとその更新を管理するために、StarNotifierクラスを作成しました。

class StarNotifier:
    def __init__(self):
        self.observers = []

    def attach(self, observer):
        self.observers.append(observer)

    def notify(self, star_event):
        for observer in self.observers:
            observer.update(star_event)

このクラスは、イーサンが観測者を付けたり外したりして、天体現象が発生したときに通知することができるものでした。

啓示が解き明かされる

EthanはStarNotifierにオブザーバーを取り付け始め、やがて天体の出来事を解釈し始めました。

star_notifier = StarNotifier()
star_notifier.attach(StarAlignmentObserver())
star_notifier.attach(CometSightingObserver())
star_notifier.attach(LunarEclipseObserver())

star_notifier.notify("StarAlignment")
star_notifier.notify("CometSighting")
star_notifier.notify("LunarEclipse")

観測者たちは、そのサインを読み解きながら、村は来るべき出来事に備えていた。繁栄と成長の知らせに喜び、尊敬する訪問者を歓迎し、前途多難を覚悟する。

おわりに 星からの知恵

イーサンはオブザーバー・パターンを発見し、星からのサインを効率的に解釈することで、刻々と変化する村の舵取りを支援した。星々の知恵は、村の繁栄、困難、そして新たな出発を導いてくれたのです。

3_8 state「感情によって能力が変わるスーパーヒーローの冒険」

A_powerful_superhero_surrounded_by_a_vivid_aura_represen.jpg

感情の移り変わりを表す鮮やかなオーラに包まれたスーパーヒーローが、混沌とした街並みの中で脅威のヴィランと戦い、市民は畏怖と希望を抱いている。

感情の力

スーパーヒーローが正義のために戦う世界で、ひときわ異彩を放つヒーローがいます。そのヒーローは、感情によって力を変えることができるユニークな能力をもっているのです。怒ると驚異的な力を発揮し、冷静になると人を癒し、興奮すると超音速で空を飛ぶことができます。

class HeroState:
    # このメソッドは、ヒーロー状態のベースクラス
    def handle_power(self, hero):
        pass

class AngryState(HeroState):
    # このメソッドは、ヒーローの状態を「パワーステート」に変更する
    def handle_power(self, hero):
        hero.strength = "パワー"

class CalmState(HeroState):
    # このメソッドは、ヒーローの状態を「回復ステート」に変更する
    def handle_power(self, hero):
        hero.healing = "回復"

class ExcitedState(HeroState):
    # このメソッドは、ヒーローの状態を「バランスステート」に変更する
    def handle_power(self, hero):
        hero.speed = "バランス"

class Hero:
    def __init__(self):
        self.state = None

    def set_state(self, state):
        self.state = state

    def change_power(self):
        self.state.handle_power(self)

困っている街

ある日、街は強力な悪党の集団に襲われる。主人公は、自分の能力を賢く使って窮地を脱しなければならないと考え、行動を開始する。悪党たちが引き起こす破壊を観察しているうちに、彼は怒りを覚え、力を増していく(パワーステート)。

hero = Hero()
hero.set_state(AngryState())
hero.change_power()
print(hero.strength)  # パワー

バランス感覚を養う

戦いが激化するにつれ、主人公は力だけでは悪党を倒せないことに気づく。自分の能力を最大限に発揮するためには、感情のバランスを取る必要がある。そんな中、ふとした瞬間に傷ついた市民を癒し、彼らの信頼を得る(回復ステート)。

hero.set_state(CalmState())
hero.change_power()
print(hero.healing)  # 回復

最終決戦

街の希望は主人公の手に託され、最後の戦いに挑む。興奮冷めやらぬまま、主人公は大空を駆け巡り、悪党の巣窟を目指します。激しいバトルが繰り広げられる中、主人公は感情を揺さぶりながら、あらゆる力を発揮する(バランスステート)。

hero.set_state(ExcitedState())
hero.change_power()
print(hero.speed)  # バランス

凱旋とレガシー

感情の力と能力を支配するState(状態/ステート)パターンによって、主人公は悪党を倒し、街を救います。彼の物語は、能力の強さだけでなく、私たちを駆り立てる感情こそが、真の強さであることを思い出させるものである。こうして、変化し続ける力を持つヒーローの遺産は続いていくのです。

このステートパターンは、ヒーローが感情に基づいてシームレスに能力を切り替えることを可能にし、彼が直面するどんな困難にもダイナミックで適応的な解決策を提供します。

3_9 strategy「チェス大会で優勝を目指す少年の戦略的な物語」

In_this_painting_two_young_focused_chess_players_sit_fac.jpg
この絵では、集中力のある若い2人のチェスプレーヤーが、机を挟んで手強い相手と向かい合って座り、机に置かれたチェス盤の上に手を置いて次の一手を考えているところを、チェスマスターが傍観している。

少年の夢

昔々、ある小さな町に、ティムという名の少年が住んでいました。ティムは聡明で好奇心旺盛な子供で、チェスに熱中していました。彼の夢は、町が毎年開催するチェス大会で優勝し、チェスチャンピオンの称号を手に入れることでした。

チェスの達人

ある日、ティムは引退したチェスのグランドマスターである老人に出会い、戦略的なチェスのプレイを教えることに同意した。チェス・マスターはティムに、ゲームのさまざまな状況に合わせて、さまざまな戦略を教えた。彼は、相手の動きを理解し、それに合わせることが、試合に勝つための鍵であると強調した。

戦略パターンを応用する

ティムは練習を重ねるうちに、各戦略がPythonのクラスのようであり、チェスの一手がゲームの状態に応じて適用できるメソッドのようであることに気づいたのです。チェスマスターの教えは、異なるアルゴリズムを選択し、相互に適用できる「戦略パターン」に似ていたのです。

from abc import ABC, abstractmethod

class ChessStrategy(ABC):
    @abstractmethod
    def make_move(self):
        pass

class OffensiveStrategy(ChessStrategy):
    def make_move(self):
        return "攻撃的な動き"

class DefensiveStrategy(ChessStrategy):
    def make_move(self):
        return "防御的な動き"

class ChessPlayer:
    def __init__(self, strategy: ChessStrategy):
        self._strategy = strategy

    def set_strategy(self, strategy: ChessStrategy):
        self._strategy = strategy

    def execute_move(self):
        return self._strategy.make_move()

トーナメント開始

大会当日を迎え、ティムのテンションは上がっていく。相手の動きに合わせて攻守を切り替えながら、学んだ戦略を駆使して試合を進めていく。

tim = ChessPlayer(OffensiveStrategy())
print(tim.execute_move())  # 出力されます: 攻撃的な動き

tim.set_strategy(DefensiveStrategy())
print(tim.execute_move())  # 出力されます: 防御的な動き

最終戦

そしてついに決勝戦、相手は経験豊富で威圧的なディフェンディングチャンピオン。試合が始まると、ティムは攻撃的な戦略でスタートしたが、すぐに相手が防御に長けていることに気がついた。そこで、ティムは守りに徹し、じっくりと隙をうかがうことにした。

必勝の一手

ゲームが進むにつれ、Timの相手は過信して決定的なミスを犯し、重要な駒を無防備にしてしまった。その隙にティムは再び攻めに転じ、駒を捕らえ、大きなアドバンテージを得た。最終的にティムは勝利し、チェスチャンピオンになるという夢を実現した。

学んだこと

Strategyパターンは、Timの成功に不可欠な役割を果たした。ゲームの状態に応じて適応し、最適な行動を選択することができたからだ。チェストーナメントで優勝を目指すTimの物語は、人生やソフトウェア開発のように、柔軟性と適応性が困難を克服し、目標を達成する鍵であることを思い出させてくれるのです。

3_10 template_method「料理学校で独自のレシピを発展させる生徒たちの物語」

template.jpg
活気ある料理学校の厨房では、若い料理人たちがユニークな形と色彩の料理を作っている。机の片隅にはテンプレートが書かれた紙が置かれている。

グランディッシュクッキングスクール

グランディッシュクッキングスクールでは、世界中から生徒が集まり、食欲をそそる料理を作る技術を学びました。この学校の理念は、特定のレシピを教えるのではなく、"テンプレート・メソッド "を用いて、生徒が自分自身の料理の傑作を生み出す力を身につけることでした。

テンプレート・メソッド

同校の料理長であるグスタボは、生徒たちに「テンプレート・メソッド」という概念を紹介した。彼は、各料理は基本的なアウトラインに従うが、自分の好みに応じて自由にカスタマイズすることができると説明した。

from abc import ABC, abstractmethod

class DishTemplate(ABC):

    def prepare_dish(self):
        self.choose_ingredients()
        self.prepare_ingredients()
        self.cook()
        self.add_final_touches()

    @abstractmethod
    def choose_ingredients(self):
        pass

    @abstractmethod
    def prepare_ingredients(self):
        pass

    @abstractmethod
    def cook(self):
        pass

    @abstractmethod
    def add_final_touches(self):
        pass

個別レシピの紹介

テンプレートメソッドを手にした学生たちは、自分たちで作った料理の試作を始めた。例えば、Mei(メイ)という学生は、麺つゆを作ることにした。

class MeiNoodleSoup(DishTemplate):

    def choose_ingredients(self):
        print("メイは麺とスープ、そして野菜とスパイスの盛り合わせを選ぶ。")

    def prepare_ingredients(self):
        print("メイは野菜を切り、スパイスを量る。")

    def cook(self):
        print("メイはスープを煮込み、麺を茹でる。")

    def add_final_touches(self):
        print("メイはスープに新鮮なハーブを添えて、蒸し焼きにして提供する。")

別の生徒のRaj(ラジ)は、スパイシーなカレー料理を作ることを思いつきました。

class RajCurry(DishTemplate):

    def choose_ingredients(self):
        print("ラジは様々な野菜、スパイス、ココナッツミルクを選びます。")

    def prepare_ingredients(self):
        print("ラージは野菜を切り、スパイスを挽く。")

    def cook(self):
        print("ラジは野菜とスパイスを炒め、ココナッツミルクを加えて煮込む。")

    def add_final_touches(self):
        print("ラジは、カレーをふっくらとしたご飯の上に盛り付ける。")

料理展の様子

学期が終わると、学校は生徒たちの作品を紹介する料理展を開催しました。メイとラジは、クラスメートとともに、食通の聴衆の前で自分たちの料理を披露しました。

参加者たちは、それぞれが独自の工夫を凝らした多様な料理に驚きました。生徒たちの創造性と、グスタボシェフの「テンプレートメソッド」による指導方法に拍手喝采が送られました。

まとめ

グランディッシュクッキングスクールの生徒たちは、「テンプレートメソッド」と呼ばれるパターンによって、基本的なアウトラインに沿って、自分の好みに合わせてカスタマイズすることで、自分だけの料理を作ることを学びました。そうすることで、彼らは料理への深い理解を持ち、美食の世界で活躍する熟練したシェフになることができたのです。

3_11 visitor「異次元からの訪問者が地球の文化に触れる物語」

Anime._In_a_warm_and_lively_town_square_a_diverse_group.jpg
温かく活気のある町の広場で、音楽家、画家、料理人など多様な人々が、神秘的でありながら親しみやすい人物を囲み、独自の習慣や伝統を共有しながら、活気と調和のある祝賀会を開いています。

来訪者の到着

あるのどかな小さな町に、異次元からの謎の訪問者が現れました。この訪問者は「Voy(ヴォイ)」と呼ばれ、異次元間の理解と友好を目的に、地球の文化や伝統を学ぶことを使命としていました。ヴォイは、異なる習慣や慣習に適応し、環境を破壊しないユニークな能力を持っていたのです。

地球人との出会い

ヴォイはまず、さまざまな場所を訪れ、地元の人々と交流し、その習慣や伝統を学ぼうとしました。ヴォイは一人ひとりと関わりながら、その人ならではの知識や経験を訪問者に伝えていきました。

class EarthInhabitant:
    def accept(self, visitor):
        visitor.visit(self)

class VoyVisitor:
    def visit(self, inhabitant):
        pass # 特定の訪問者クラスで実装されること

異文化を体験する

学習能力の高いヴォイは、出会った人々の習慣をすぐに理解しました。そのため、さまざまな文化的背景を持つ人たちと関係を築くことができました。

class Musician(EarthInhabitant):
    def accept(self, visitor):
        visitor.visit(self)

class Painter(EarthInhabitant):
    def accept(self, visitor):
        visitor.visit(self)

class Chef(EarthInhabitant):
    def accept(self, visitor):
        visitor.visit(self)

ボイは旅を続けながら、出会う人ごとにアプローチを変えていき、それぞれの文化のニュアンスを理解することができました。

class MusicVisitor(VoyVisitor):
    def visit(self, musician):
        # ヴォイ learns about music from the musician
        pass

class PaintingVisitor(VoyVisitor):
    def visit(self, painter):
        # ヴォイは画家から絵について学ぶ
        pass

class CookingVisitor(VoyVisitor):
    def visit(self, chef):
        # ヴォイはシェフから料理について学ぶ
        pass

ナレッジの共有

地球の多様な文化について知識を深めたヴォイは、その経験を出会った人たちと共有するようになりました。その結果、互いの背景や伝統をより深く理解し、感謝するようになったのです。

def learn_about_culture(earth_inhabitant, visitor):
    earth_inhabitant.accept(visitor)

musicians = [Musician() for _ in range(3)]
painters = [Painter() for _ in range(3)]
chefs = [Chef() for _ in range(3)]

music_visitor = MusicVisitor()
painting_visitor = PaintingVisitor()
cooking_visitor = CookingVisitor()

for musician in musicians:
    learn_about_culture(musician, music_visitor)

for painter in painters:
    learn_about_culture(painter, painting_visitor)

for chef in chefs:
    learn_about_culture(chef, cooking_visitor)

調和のとれた結末

ヴォイの旅はやがて終わりを告げたが、彼らが町に与えた影響は計り知れません。ヴォイが出会った人々は、ユニークな訪問者のおかげで、新しい視点と異文化への理解を深めていくことができました。そして、お別れに、町の人たちが集まって、それぞれの才能を発揮し、盛大に祝ったと伝えられています。

ビジターパターンによって、ボイは地球上のさまざまな住民とスムーズに交流し、彼らから学ぶことで、訪問者と出会った人々の双方が互いを理解し、感謝するようになりました。このつながりは、次元を超えた永遠の友情につながったのです。

さらなる学習用資料

この本の作り方

この本は以下のようなステップで作りました。

  • もっといい方法があったら教えてください(Twitter/プルリク、なんでもいいです)
    • またもっといい物語や画像ができたらぜひプルリクしてください。まるごと差し替えます

作業手順

  1. まず各パターンに対してテーマを適当に決めてもらう(ChatGPT)

    • プロンプト
      GoFデザインパターンの各デザインパターンに対して物語を作ってもらいたいと思いますが、それに先立って各デザインパターンでどのような物語を書くつもりかを1物語30字程度にまとめたうえで羅列してください。なお、似たような物語が存在しないようにジャンルや設定はできるだけ幅を持たせてください。
      
      • 今回の時の回答
        1.2. Factory Method: 宇宙船製造工場でのロボット労働者の冒険
        1.3. Abstract Factory: 魔法のキャンディ王国での製菓家族の物語
        1.4. Builder: 砂漠のオアシスでの家を建てる旅人の挑戦
        1.5. Prototype: クローンを作る未来都市での個性を見つける探求
        1.6. Adapter: ジャングルで異なる動物たちが協力して生きる話
        1.7. Bridge: 分断された2つの島をつなぐ橋を築く住民の奮闘
        1.8. Composite: 雪の森で木々が連携して脅威に立ち向かう物語
        1.9. Decorator: クリスマスツリーの飾りが命を宿す不思議な話
        1.10. Facade: 壁の向こうの秘密の世界への入り口を~~~
        
  2. 各パターンに対して物語を語ってもらう(ChatGPT)
    効率化のために英語で質問しています。

    • プロンプト(英語。この場合はVisitorパターン。パターン名'Visitor Pattern'と1の回答結果を英訳したもの('The story of a visitor~)をプロンプト内に埋め込んでいます)
      Tell the story of the GoF design pattern 'State Pattern' as a 500-word episode that shows how the design pattern works in the human drama 'The adventures of a superhero whose abilities change with his mood.'. Please set some  commented python code in moderation to make it easy to understand how it works. Please add headings according to the content.The style of writing should match the content of the story.
      
      プロンプトの意味(日本語訳)
      GoFのデザインパターン「State Pattern」が、人間ドラマ「気分で能力が変わるスーパーヒーローの冒険」でどのように機能するかを500字のエピソードとして語ってください。仕組みがわかりやすいように、コメント付きのpythonコードも適度に設定してください。内容に応じて見出しを付けてください。文体はストーリーの内容に合わせてください。
      
    • 記事が生成されるのでDeepL等で翻訳してください
      • である・ですます調が混在しがちなので、物語に向いている方に書き直したほうがいいかもしれません
  3. 直後の質問で画像生成AI用のプロンプトを作成してもらう(ChatGPT)

    • プロンプト

      Assuming that a scene from this episode is to be represented as a painting, please provide a short description (one sentence) of the painting. However, please observe the following [Restrictions].
      
      [Restrictions].
      - Do not include names of people or other proper nouns.
      - Do not mix multiple scenes in the screen.
      - Please start with a general overview and describe the details step by step.
      - Please write in English.
      

      意味(日本語訳)

      このエピソードの1シーンを絵画として表現することを想定し、その絵画の簡単な説明(1文)を記入してください。ただし、以下の【制限事項】をお守りください。
      
      【制限事項】
      - 人名などの固有名詞を含めないこと。
      - 画面内に複数のシーンを混在させないでください。
      - 全体像から始めて、段階的に詳細を記述してください。
      - 英語で書いてください。
      
      • 回答例
        A powerful superhero, surrounded by a vivid aura representing his shifting emotions, battles menacing villains amidst a chaotic cityscape, with citizens looking on in awe and hope.
        
  4. 画像生成AIでプロンプトを張り付けて画像を生成する

    • 必要に応じて好みの画像のタイプ(例:Anime,)などを指定
      2023-03-29-15-13-54.png
    • 具体的なスタイルについてはこちら等が参考になります。
    • 生成された画像例
      howto_image_result.jpg

さいごに

ChatGPT(GPT-4)とMidjourneyを使っていてこの本のアイデアを思いついてから2日で仕上がりました(作業時間12時間、そのうち半分くらいはChatGPTのエラー解消待ちやMidjourneyプロンプトがうまくいかない事への対策でした)。これから私もこの本やほかのコンテンツでもっとデザインパターンへの理解を深めたいと思います。
従来の「学んでから本を書く」というプロセスから、「本を書いてもらってから学ぶ」というSFめいた事が本当にできてしまいました。このデザインパターンに限らずあらゆる事の学習に対し適用できるパターンだと感じています。ChatGPTを筆頭とするLLMツールは今後の生活に欠かせないものだと思いますので、まだこの手のツールに慣れていない方には使用し始めることをぜひおすすめして最後の言葉とさせていただきます。
(2023年3月29日)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12