ちゃなべの備忘録

ほぼ備忘録です。

GoFのデザインパターンの記事を読んでみて【備忘録】

www.techscore.com

1. Iterator パターン

www.techscore.com

listのオブジェクトを用意するときに使うパターン。
「ID順」「作成順」などいろいろな走査パターンがあるが、それをListクラス自体に用意するのは柔軟性がない。

なので、Listクラスとは別に走査を管理するクラスを作ることによって柔軟性を持たせるパターン。

  • Listクラス自体は変化しうるもの
  • Listクラスの走査パターンも変化しうるもの

という前提であれば、Iteratorパターンはとても有効。Listクラスによって、使う側がコードを変えなくて済むようになる。

たぶん本質はAggregateIFとIteratorIF。この二つがListという概念をいい感じに抽象化している。

2. Adapter パターン

www.techscore.com

インターフェースに互換性がないクラス同士を組み合わせるときに使うパターン。

...そんなときあるんか?とも思うが、「これまで使っていたメソッドの上位互換のようなメソッドを持つクラスがあった場合、インターフェースの違いを吸収するためにAdapterを準備する」のような使い方をするっぽい。

やり方は継承を使うパターンと委譲を利用するパターン。それぞれ方法は違えど、やっていることは これまで使っていたメソッド を利用するだけ。

3. TemplateMethod パターン

www.techscore.com

templateとは文字の形に穴が空いている薄いプラスチックの板を意味しており、マジックで書くか鉛筆で書くかは自由だけど製作物の形は決めちゃうぜっていうもの。

TemplateMethodは、

  • 固定で決めるもの=メソッドを使った処理のフロー
  • 自由に後から決めるもの=メソッドの具体的な実装

です。 例えば「カレーを作る」というものがあったら、

public abstract class CookCurry{
    public abstract void cut( Ingredient[] yasais );
    public abstract void niru( Ingredient[] yasais );
    public abstract void putSpice( Ingredient[] yasais, Spice spice );
    public void createWoodCutPrint(){
        Potato potato = new Potato(); // Potato クラスは、Ingredient インタフェースを実装している
        Carrot carrot = new Carrot(); // Carrot クラスは、Ingredient インタフェースを実装している
        Ingredient[] ingredients = {potato, carrot};
        Spice spice = new Spice();
        cut( ingredients );
        niru( ingredients );
        putSpice( ingredients, spice);
    }
}

このスーパークラスを満たすようにサブクラスでメソッドの具体的な実装を定義したら完了。

4. FactoryMethod パターン

www.techscore.com

3の場合ではメソッド自体しか自由度がなかったのですが、処理内のオブジェクト生成にも自由度を与えたい時に使うのがFactoryMethodパターンです。

3の例を使うと、

public abstract class CookCurry{
    public abstract void cut( Ingredient[] yasais );
    public abstract void niru( Ingredient[] yasais );
    public abstract void putSpice( Ingredient[] yasais, Spice spice );
    public Ingredient[] createIngredients() {
        Potato potato = new Potato();
        Carrot carrot = new Carrot();
        Ingredient[] ingredients = {potato, carrot};
        return ingredients;
    }
    public Spice createSpice() {
        return new Spice();
    }

    public void createWoodCutPrint(){
        Ingredient[] ingredients = createIngredients()
        Spice spice = createSpice();
        cut( ingredients );
        niru( ingredients );
        putSpice( ingredients, spice);
    }
}

createSpice、createIngredientsをサブクラスでオーバーライドできる。

5. Singleton パターン

www.techscore.com

クラスのインスタンスが一つしかないことを保証するためのパターンです。
コンストラクタを private とすることで、他クラスから新たにインスタンスが生成されないような構造とすることで、インスタンスの生成を制御します。

Javaではインスタンスを作成する時に、 new Hoge()とするがそれをできないようにさせる。
代わりにHoge.getInstaunce()のように取得させるようにし、返すインスタンスは何度やったとしても一つの既存の作成済みインスタンスしか返さないようにするというものだ。

6. Prototype パターン

www.techscore.com

同じ作業はコードや関数として落とし込むんじゃなくて、オブジェクトのメソッドやコンストラクタとして落とし込んだ方がめっちゃ楽だよねって話。

そりゃそう。

7. Builder パターン

www.techscore.com

家の建築でいうところの、「家の建築過程」と「素材」の役割を明確に分離するイメージです。

作成過程を Director、表現形式(上でいう素材)を Builder に任せて、それぞれを自由に組み替えられるようにすることで柔軟性を生みます。

これTemplateMethodパターンと似ているんですが、違いとしては以下の2つ

  • Builderパターンの目的はオブジェクトを生成することであり、アルゴリズムを実行することではないこと。
  • Builderパターンではインターフェースとして分離しているが、TemplateMethodは継承を使って分離している

あと名前もいいね、わかりやすい。DirectorとBuilderって。

最終的にDirectorの引数に具体的な実装のBuilderを注入して実行する形になります。

8. AbstractFactory パターン

www.techscore.com

これってBuilderパターンと達成したい目的が全く一緒な気がするんだが??
と思っていろいろ調べてたんですが、個人的にはこのサイトがすごいしっくりきました。

techracho.bpsinc.jp

イメージは以下のような感じ。ハンバーガー屋さんに例えました。

  • Builderパターン → バンズ、パティ、ソース、野菜は全部お客さんが自由に選べます!選んでもらったらこっちで作ります。
  • AbstractFactoryパターン → 照り焼きバーガー、チーズバーガー、お月見バーガーなどのメニューを選んでください!作り方と中身はメニューに合ったものをこちらでやります。

みたいな感じかな。どっちを選ぶかは方針次第、複雑度や制約性などを考えながらだと思います。クライアントに見えている部分の抽象度が違うね。

実装自体は製作過程と表現方法を両方とも一括のインターフェースを定義して、その実装をパターン分だけ用意する感じ。

9. Bridge パターン

www.techscore.com

例えば色が2種類選べるバッグがあるとする。その時クラスは2つなのだが、もしオプションでポケットをつけれるとしたら、クラスは2x2の4種類になってしまう。そこから機能が追加されればされるほどクラスが膨大になるの?? という問題に対してアプローチした解決策。

ぶっちゃけFactoryMethodと同じような感じがするが、こっちは「拡張性」に重きを置いた感じかと思います。「現状は想定がないけど、もし拡張したいときは簡単にできるように」という想定。

実装にはインターフェースを使いません、継承と埋め込みを使います。

public class Bag {
    private BagImple bagImple;
    public Bag(BagImple bagImple){
        this.bagImple = bagImple;
    }
    public void getColor() {
        bagImple.getColor();
    }
}

public abstract class BagImple{ 
    public abstract String getColor(); 
}

public class WhiteBagImple extends BagImple {
    public String getColor() {
        return "white";
    }
}

public class BlackBagImple extends BagImple {
    public String getColor() {
        return "black";
    }
}

これに対してポケット付きのバッグを定義すると?

public class PocketBag extends Bag {
    public PocketBag(BagImple bagImple) {
        super(bagImple);
    }
    public String getPocketColor() {
        return getColor();
    }
}

これだけで済むね。

10. Strategy パターン

www.techscore.com

とある実装のアルゴリズムの切り替えを容易にする時に使います。というかさっきのBridgeパターンのWhiteとBlackを切り替えているような実装がまんまです。

ソートのアルゴリズムを容易に変えたい時、ゲームのレベル(アルゴリズム)を容易に変えたい時などに使われるらしい。

まぁやっていることは簡単でインターフェースを定義してそれを満たすようにアルゴリズムをそれぞれ書いて、必要なアルゴリズムを注入する感じ。

11. Composite パターン

www.techscore.com

自身を含む複数種類のクラスを再帰的に参照をする構造の時に使う。「ファイルシステムを作る時に使うパターン!」と覚えておいたら結構楽かも。

再帰的に参照をするクラスのインターフェースを定義しておいて、それに対して実装を行うことで楽になる。

12. Decorator パターン

www.techscore.com

一つのコアに対して装飾をどんどん追加していきたいときに使います。「クレープにトッピングをどんどんしてくパターン!」と覚えておいたら楽かも。

解決策がCompositeパターンのように再帰を行うので、どっちを使えばいいのかわからなくなりそう。Compositeパターンとの違いはたぶん以下。

  • Decoratorパターン → 一つの被オプションに対してオプションだけをどんどんつけていくイメージ
  • Compositeパターン → 被オプションに対してオプションをつけることもできるし新たな被オプションをつけることもできるイメージ

13. Visitorパターン

www.techscore.com

なんかこれめちゃめちゃむずくないか。本質を捉えるのが大変だった。たぶん嬉しいことが、Visitorのvisitっていうメソッドと、Accepterのacceptっていうメソッドを作るだけで、お互いやりたいことが自由にできるってことだとは思う。

けどこれってIFを通じて双方向に依存しているだけな感じがしてならない、果たして美しいのか。

参考↓

refactoring.guru

14. Chain of Responsibility パターン

www.techscore.com

責任の鎖をつなぐようなパターン。社員→課長→部長→社長と判断できるところまで稟議が上がっていくような設定を組むことができる。

実装自体は簡単で、nextを設定できるようなsetNext()メソッドをIFを共通で設定しておいてあげるだけ。

15. Facadeパターン

www.techscore.com

Encodeがめちゃめちゃ複雑な実装だとしても、それを利用する側からしたらencodeというメソッドを実行するだけで済むようにしてあげたほうがいいよね、っていう当たり前のお話。

16. Mediator パターン

www.techscore.com

飛行機と管制塔の関係に似ています。飛行機は飛行機同士で着陸のタイミングを連絡し合うのは至難の技です。しかし、飛行機は管制塔と連絡しさえすれば管制塔が支持を出してくれます。

↑のように複雑な関係性を一つの調停者が管理することで、依存関係を制御することができます。

具体的な実装は、各オブジェクトは調停者のIFに依存し、調停者の実装はオブジェクト共通のIFに依存し、IFを通じて会話をします。

17. Observer パターン

www.techscore.com

親が子のことをずっと監視するのではなく、子が親に報告するようにしたら、親の管理が楽だよねって話。

イベントあったら発行してね的な感じ。

18. Memento パターン

www.techscore.com

「あの時これやったなぁ」を記憶しておいて、過去にやったことをすぐに利用できるようにして、同じことを2度やらないようにしたパターン。

例えば、足し算のようなもの。 「1 + 2 + 3」をしたら、それをMementというオブジェクトにアウトプットして、辞書型配列に名前をつけて保存しておく。 「1 + 2 + 3」を使った計算をしたかったら、辞書型配列から呼び出して、Mementを復元して計算に加える感じ。

19. State パターン

techscore.com

状態ごとにメソッドや値が変化する時、その状態を条件としてメソッドや値の条件分岐を実装するのが一般的だが、状態のパターンを増やした時条件分岐も増やさないと行けないのが厄介である。

よって各状態ごとのクラスを定義しておけば、そのクラスの条件分岐をするだけで済むよねってお話。

20. Flyweight パターン

www.techscore.com

キャッシュのようなもの。重たくて何度実行してもレスポンスが変わらないメソッドの場合、一度実行した結果はどこかに持っておいて、2度目以降呼び出された時はそれを返すようにしようってやつ。

21. Proxy パターン

www.techscore.com

Proxy は代理人という意味です。クライアントからのリクエストを代理人が受けとり、代理人が答えられるものは答えて、答えられないものは本人に聞いて、その回答を利用して答えるようなものです。

Chain of Responsibilityパターンとすごい似ている感じがしますが、違いは以下

Proxy Chain of Responsibility
連結するクラス 原則2つ
代理人と本人
いくらでも
上位のクラスの呼び方 直接呼ぶ passすれば実行が自動的に上位に移る
メソッドの実行 上位クラスの結果を利用できる 上位クラスの結果を利用できない

ごめん、ぜんぜん似てなかったわ。

22. Commandパターン

www.techscore.com

「あるオブジェクトのメソッドが今後増える可能性がある時、そのメソッドをオブジェクト内で定義するのではなく、関数として独立して定義しその関数の引数にオブジェクトを渡してあげるようにすれば良くね?」というのがCommandパターン。

「オブジェクトのメソッド」というよりも「オブジェクトを操作する」というようなニュアンスが増えた感じだね。

23. Interpreterパターン

www.techscore.com

構文木で使われたりするパターン。だけどCompositeパターンと構造はほぼ全く一緒。Compositeは add() メソッドがあるが、Interpreterには特にない、くらいかな。

Compositeパターンはフォルダ構造とかを表現する時に使って、
Interpreterパターンは構文木構造とかを表現する時に使うよ。