懐かしの パタンランゲージ を眺めつつ、ふと 再考: GoF デザインパターン が気になったのでさらっと流しておこう。
[amazonjs asin=”4306041719″ locale=”JP” title=”パタン・ランゲージ―環境設計の手引”]
そもそもが GoF 発祥自体が 90年頃で、日本語の書籍は Java が発生した頃と前後してしまっているので、オブジェクト思考ブーム、UML, パターンのブームよりも随分前からある。C# や Java よりも前になので、今さら再考する意味もないのではないか?と思われるのだが(そもそも Qiita の「再考~」の記事自体が 2014 年だ)、歴史経緯から考えるのと、GoF の反省(あるいはドツボ?)からできあがった現在の C#, Java の言語拡張をみていくと、これらのパターンが言語仕様やライブラリに含まれてしまっていることが分かる。逆に言えば、.NET ライブラリを使うときに「?」とか「何でこんなややこしい使い方になっとるんやッ!!!」というときには、ここの GoF を踏まえたライブラリづくりがあって現在があるという経緯をを知ると、なんとなく納得がいくと思う。良くも悪くも、最適化(準かもしれないけど)されつつあるということだ。
それは、アルゴリズムのテクニックとして、双方向リストやらソートやらが流行っていた時期があって、今だと、List<> や Sort() で十分な訳で、実用的にはそれでよいし、でも知識として知っておくとその延長がわかるという仕掛けが含まれている。
というわけで、GoF を現在の C#, .NET クラスライブラリと合わせてさらっていこうと思う。
Abstract Factory
Factory パターンの汎用版で、C# だとストリームを作るときに使ってる。UWP の File.Open あたりがそれ。
Builder
なんとか Builder が好きな人が多くて StringBuilder とかに出てくる。
Factory Method
いわゆるファクトリーパターンで、Java だと結構頻発する。C# だと TaskFactory.StartNew にみられる。new するときにパラメータが多くなるのを嫌がったパターンと、DOM で Parent を Document にするときに作るときに使われる。
Abstract Factory と比べると、Factory/Creator クラスが二重化されていることがわかる。
Prototype
見かけたことがない…というか、普通にインターフェース。
Singleton
static で十分やろ、ってな感じで使われている。
Adapter
継承を使ってクラスを拡張する代わりに、あらかじめ拡張するメソッドをインターフェースにしておく手法。メッセージングのときに、後付けで必要なインターフェースを追加するときに使う。データ自体と操作するメソッドを分離するときによこう使われる。が、最初からわかっていれば、Interface で十分な気も。
Bridge
今だと普通に委譲を使うのだが、GoF の発祥自体が Java, C# よりも前なのだ。C++ と同時期だと思えば、そういうパターンがあっても仕方がない。継承地獄を避けるためにつくられる。今となっては常識になったパターン…だが、.NET クラスライブラリには歴史的にか継承地獄が頻発する。
Composit
今でいえば、DOM そのもの。ツリー構造を使いたいときは、このパターンを使うか、DOM を使う。
Decorator
元クラスがぴよぴよクラスのときに、継承して拡張するか、メンバにして拡張するか、のパターン。
Facade
残念ながら .NET クラスライブラリにはこの発想はない。既定クラスからばらばらとメソッド&プロパティが公開されている。Android の ViewGroup へのキャストがそれ。あるいは、ごちゃごちゃになったクラスをクライアントから見れば整理するパターン。ユーティリティクラスがそれ。
Flyweight
データベースのコネクションプーリングで使われている。
Proxy
いわゆる、アスペクト指向のパターンなのだが、C# の UI コントロールのメソッドが常に override と event を持っているのが proxy の役割になっている。あらかじめ仕込んでおかないといけないのがミソ。
Chain of Responsibility
複数のオブジェクトを連ねていくパターン。クリップボードのチェーンや、昔の常駐プログラムのキーチェーンで使われていた。いわゆる協調パターン。
Command
MVVM パターンにも出て来るおなじみの、ICommand インターフェースになる
Interpreter
いわゆる、式木だが、普通の人は使わない。動的にスクリプトを使うとか一部の関数型言語マニアが使う。F# だと簡単に作れる。
Iterator
いわゆる C++ のイテレーターだし、C# の IEnumerator だ。今だと、LINQ を使ったほうが楽。
Mediator
リモートメッセージで使われる SOAP とか JSON とかがそれにあたる。DCOM, CORBA を知っていればシリアライズという形で現れる。
Memento
いわゆる、Undo/Redo に使われるパターンなのだが、これを使った実装は見たことがない。そもそも Undo/Redo 自体が難しくて、今だとメモリのある限り丸ごと保存してしまう。例えば、Visual Studio をコンテナとして Undo/Redo メソッドとして利用する、ような感じ。
Observer
これも MVVM パターンで使われる ObservableCollection<T> の元ネタになる。
State
switch や if で実装しないために State パターンを使う。インターフェースを作っておくと、無限種類でオブジェクトを作れるので、State パターンは便利なのですよ。5 個程度ならば swtich、それ以上であれば State パターンを使えばよい。
Strategy
かつて openssl で使っていたパターンで、暗号コードの構造体を切り替える。ちなみに openssl は C言語だ。部分的に切り替えるのが State で、ごっそり切り替えるのが Strategy になる。ただし、.NET クラスライブラリに Strategy という単語はでてこなくて、Factory あたりでごっそりクラス名を切り替えたりする。ストリームクラスあたりがそれ。
Template Method
interface だったり、abstract だったりするが、言語仕様として Interface は内部フィールドを持たず、Abstract は内部データを持てる、という違いがあったりする。言語の都合なのと、IDE の補完機能を使って自動メソッドを使うときに便利なパターンだ。
Visitor
このあたりは、ほとんど delegate と一緒だったり、Action<>, Func<> で大丈夫だったりするが、異なるスレッドの場合はディスパッチが発生するため、このパターンが使われる?たぶん、C# だと Invoke になると思う。
改めて見直すと、コンテナとオブジェクトのパターンが多い。「パターン」自体が、頻出されるモデルという意味合いなので、かつてのオブジェクト指向以前であれこれと悩んだ末に出てきた定型パターンと考えられえる。定型だからこそ、現在の言語仕様やライブラリではそれらのパターンを取り入れてしまって、外側に出ないようにする。そういう意味では、GoF のパターンは形骸化しているといってよい。が、新しいプログラム環境で作ろうと思ったり、もっと大きい範囲でパターンを考える(ロボット制御の ROS とか、スマートフォンや PC などのメッシュ的な通信とか)ときには、これらのパターンが有効に働くので、知識として覚えておくとよいだろう。
おまけ
astah で作った GoF の図を置いてきます。
参考先
- Design Patterns 15 Years Later: An Interview with Erich Gamma, Richard Helm, and Ralph Johnson
http://www.informit.com/articles/article.aspx?p=1404056 - Strategy pattern vs. Command pattern
http://stackoverflow.com/questions/3883692/strategy-pattern-vs-command-pattern/3896839#3896839
[amazonjs asin=”B000SEIBB8″ locale=”JP” title=”Design Patterns: Elements of Reusable Object-Oriented Software (Adobe Reader)”]