初投稿です
#はじめに
以前から、Java言語で学ぶデザインパターン入門の輪読会を実施していましたが、最近完遂することができました。
本記事では、輪読会のまとめとして、**各デザインパターンを「一言で表現する」**という取り組みをやってみようと思います。
狙いとしては、
・「一言で表現する」という要約の過程で、さらに理解を深めることができる
・一言であれば後から思い出しやすい
という感じです。
「一言」には、メタファーは基本的に使わないようにしました。察しの悪い私は、「飾りつけをする」とか言われても何も思い出せないのです。
間違いなどあれば、コメントいただけると幸いです(実務経験2年に満たない若輩者なので、お手柔らかにお願いします・・)
一つ一つのデザインパターンについてのまとめ記事は、輪読仲間の@tonightooにお任せしています(※記事の終わりにリンクを貼っておきます)。
こまめに記事を投稿できる人、尊敬です。
#デザインパターン 一言で表現すると
##1.Iterator <数え上げロジックの統一>
For, For Each, while … 数多くある数え上げの書き方を統一しましょう、というパターン
数え上げられるデータの構造が変更される際の、メインロジックの修正を不要にすることが狙い。
.Netであれば、IEnumerableやIEnumeratorが標準で用意されている。
##2.Adapter <部品を任意のAPIにあてはめ、再利用する>
書いた通りです。
具象クラス名をソースコードに書いたら、このパターンは適用できなくなる。
インターフェースに対してコーディングするようにしましょうね。
##3.Template Method <処理の実行順序だけ決めておく>
抽象メソッドの実行順序を定めておいて、各メソッドの実装は後付けする。
処理内容は色々だけど、順序は共通(なので何回も書きたくない)よね~、というときに使われる。
DRY原則を満たしつつ、メインロジックからの呼び出しを容易にする(メインには実行順序を意識させない)狙いがあると思っている。
##4.Factory Method <インスタンス生成時に必要な手続きをまとめる>
インスタンス生成と同時にやらないといけない手続きをまとめて、Factoryにやってもらうパターン
例えば、相手によって渡すインスタンスを変える判断をしたりとか、インスタンスを渡した相手を記録したりとか・・
##5.Singleton <インスタンスを不必要に何度も作らない>
あるクラスのインスタンスがプロセス内に唯一つであることを保証する。
適用タイミングとしては、
・インスタンスが複数存在してはいけない場合
・インスタンスが複数存在する必要が無い場合(メモリが無駄)
という感じだと思うが、今のところ必要性を感じたことが無い。
Singletonの利用者が、Singletonを介して結合してしまうデメリットが大きい。
※上記を防止するため、Singletonは複数の状態を取るような設計にしてはいけないと思う。
粒子の名前みたいでかっこいい。
##6.Prototype <インスタンスのコピーを作る>
インスタンスのコピーを作る。これによって、インスタンス生成を再度実行しなくてよくなる。
インスタンスの生成手順が複雑な場合に使われる。
.Netであれば、IClonableが標準で用意されている。
DeepCopyとShallowCopyの区別に注意!
##7.Builder <インスタンスの生成手順を抽象化する>
インスタンスの生成手順(設計図のイメージ)を抽象化し、手順の実装(建材のイメージ)から分離する。
様々な建材に対して、生成手順の使い廻しを可能とする。結果、何度も同じ手順を書かなくてもよくなる。
同じ設計図でも、建材が変われば、出来上がる建物が変わってくる、というイメージ。
##8.Abstract Factory <工場自体を切り替えて、生成するインスタンスを変える>
Factory Methodをさらに抽象化した感じ。Factory自体を抽象化。
##9.Bridge <APIの実装と、継承による機能追加を、別々の層に分ける>
機能クラスと実装クラスを、それぞれ独立に追加/修正できるようにする。
これらが分離されていないと、機能クラス×実装クラス の数のクラスが必要になるが、
分離することで、機能クラス+実装クラス だけのクラスを用意するだけでOKということになる。
##10.Strategy <委譲を利用して、メソッドの実態を動的に切り替える>
必要に応じて、メソッド呼び出し時の処理内容を切り替えることで、似たようなクラスやメソッドをたくさん作らなくてよくなる。その結果、メソッドの呼び出し窓口を統一できる。
また、いろんな場所で分岐を作らなくてよくなるし、機能の追加にも、Strategyクラスの追加で対応することができる。
##11.Composite <木構造を表現する>
節(ノード)と葉(リーフ)を共通のAPIのもとに実装することで、使う側はその違いを意識せずに使用できる。
その結果、木構造をシンプルに作ることができる。
開発前に「あれ?これって木構造じゃね?」と思うことがあれば、Compositeの適用を検討しよう。
##12.Decorator <元クラスに手を加えず、委譲による機能拡張を繰り返す>
元クラスをコンストラクタで受け取り、機能を拡張して提供する。
APIを設定して、何度も繰り返せるようにする
##13.Visitor <ダブルディスパッチを使って、処理とデータを分離する>
ダブルディスパッチを使うことで、データをそれを処理する方法から、非常に独立性の高い状態で分離できる。すなわち、処理方法の追加や変更が容易。
Select文を使わずに、データとVisitor(処理方法を持っている人)の組み合わせによって自然と処理内容が決まってしまう。
Strategyではメソッドの種類や引数をAPIで規定するが、こちらは"Visit"メソッドにすべて集約される。
データは非常に独立性の高い部品になる一方で、Visitorのほうは、データ構造を理解している必要がある(=データ構造に依存している)。
##14.Chain of Responsibility <処理を数珠つなぎにして、順番に実行していく>
処理を持つインスタンスを順番につなぎ合わせて、先頭のインスタンスから順に処理を実行していく。
構造が線形リストと同じなので、「処理のリスト化」だと認識している。
処理の内容に応じて、リストの途中でリターンすることもできる(お目当ての処理が見つかった場合など)。
使いどころとしては
1.メインロジックで処理内容を選択したくない。全探索して、お目当ての処理を見つけたい
2.1ステップずつ順番に、データを加工したり、チェックしたりしたい。順序の入れ替えもしたい
みたいな感じだと思う。1と2で思想がだいぶ違う。
##15.Facade <処理をまとめて、窓口を一つ用意する>
処理を閉じ込めることで、メインロジックで複雑な呼び出しを行う必要が無くなる。メインロジックのシンプル化。
Facade自体は、沢山のクラスに依存することになるので、使い廻ししやすい部品にはならなさそう。
##16.Mediator <オブジェクト間のやり取りを仲介して、結合を弱める>
相互作用するインスタンスの間に入って、相互作用を仲介する。
Mediatorが無い場合、インスタンス間の相互作用の実装を、各インスタンスで保持することになる。見通しが悪いし、インスタンス同士が強く結合してしまう。
Mediatorを用意した場合、各インスタンスはMediatorに通知するだけでよい。相互作用の中身は、すべてMediatorが管理することになる。その結果、インスタンス間の結合を弱めることができる。
その弊害として、Mediatorの中身はめちゃくちゃなことに・・
##17.Observer <通知をする>
任意のタイミングで、監視者(購読者とも呼ばれる)に対して通知を行う。
.Netだと、IObserver/IObservableが用意されている。
が、イベントを使った方がシンプルで便利な気がする。
##18.Memento <状態を保存しておいて、復元できるようにする>
ゲームでいう"セーブ"のイメージ
実装方法が細かく指定されているわけではない。
なんらか状態を残しておくためのクラスがあれば、それはMementoなのだ?
※Mementoは「記念品」「形見」という意味のようです。
##19.State <状態遷移図を表現する>
状態をクラス化し、かつ、状態クラス間の遷移の仕組みを実装する。
デザインパターンでエレガントに書けると思いきや、複雑な状態遷移図を表現しようと思うと、結局コードも読みにくくなりがち。
一人一人のState自身が、それぞれに関係する状態遷移条件を保持するので、状態遷移条件がソースのあちこちに散らばって、状態遷移図全体の見通しが悪い。
##20.Flyweight <Signleton生成工場>
Signletonを作って渡してくれる。
常にSingletonのインスタンスを保持しているのではなく、必要になってからインスタンスを生成するイメージ。
省メモリが目的。
##21.Proxy <オブジェクトへの参照を代理人にやってもらい、扱いやすくする>
代理人にオブジェクトへの参照をやってもらうことで、色々スマートにする。
用途は多岐にわたる。重いインスタンスの生成だったり、アクセス制限だったり、メモ化だったり・・
下記リンクが参考になった
TechRacho Ruby:Proxyパターンの解説
##22.Command <命令をクラス化して、管理、追加しやすくする>
命令自体をクラス化することで、Undo/Redoの実装や、命令実行手順の記録をしやすくする。
また、新しい種類の命令の追加も容易である。
##23.Interpreter <構文木を表現する>
インタプリタを作るパターン。
インタプリタを作る以外に使い道あるのか・・?
パターン自体は好きで、サンプルを書いてて結構楽しかった。
#まとめ
GoFパターンは古いとかいい加減とか、いろいろ言われがちですが、初心者が抽象クラスやインタフェースの使い方を学ぶのにはよい教材なのかなと思ったりしています(初心者本人が言うことか?)。
パターンを全肯定してはいけないということを意識しつつ、個人的には学びが多くて、良い勉強会でした。
#参考:輪読会の議事録
第1回:Iterator
第2回:Adapter
第3回:Template Method
第4回:Factory Method
第5回:Singleton
第6回:Prototype
第7回:Builder
第8回:Abstract Factory
第9回:Bridge(準備中)
第10回:Strategy(準備中)
第11回:Composite(準備中)
第12回:Decorator(準備中)
第13回:Visitor(準備中)
第14回:Chain of Responsibility(準備中)
第15回:Facade(準備中)
第16回:Mediator(準備中)
第17回:Observer(準備中)
第18回:Memento(準備中)
第19回:State(準備中)
第20回:Flyweight(準備中)
第21回:Proxy(準備中)
第22回:Command(準備中)
第23回:Interpreter(準備中)