ふと GoF のデザインパターンを再考しておく

懐かしの パタンランゲージ を眺めつつ、ふと 再考: 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 あたりがそれ。

20160111_03

Builder

なんとか Builder が好きな人が多くて StringBuilder とかに出てくる。

20160111_04

Factory Method

いわゆるファクトリーパターンで、Java だと結構頻発する。C# だと TaskFactory.StartNew にみられる。new するときにパラメータが多くなるのを嫌がったパターンと、DOM で Parent を Document にするときに作るときに使われる。

Abstract Factory と比べると、Factory/Creator クラスが二重化されていることがわかる。

20160111_31

Prototype

見かけたことがない…というか、普通にインターフェース。

20160111_06

Singleton

static で十分やろ、ってな感じで使われている。

20160111_32

Adapter

継承を使ってクラスを拡張する代わりに、あらかじめ拡張するメソッドをインターフェースにしておく手法。メッセージングのときに、後付けで必要なインターフェースを追加するときに使う。データ自体と操作するメソッドを分離するときによこう使われる。が、最初からわかっていれば、Interface で十分な気も。

20160111_08

Bridge

今だと普通に委譲を使うのだが、GoF の発祥自体が Java, C# よりも前なのだ。C++ と同時期だと思えば、そういうパターンがあっても仕方がない。継承地獄を避けるためにつくられる。今となっては常識になったパターン…だが、.NET クラスライブラリには歴史的にか継承地獄が頻発する。

20160111_10

Composit

今でいえば、DOM そのもの。ツリー構造を使いたいときは、このパターンを使うか、DOM を使う。

20160111_11

Decorator

元クラスがぴよぴよクラスのときに、継承して拡張するか、メンバにして拡張するか、のパターン。

20160111_12

Facade

残念ながら .NET クラスライブラリにはこの発想はない。既定クラスからばらばらとメソッド&プロパティが公開されている。Android の ViewGroup へのキャストがそれ。あるいは、ごちゃごちゃになったクラスをクライアントから見れば整理するパターン。ユーティリティクラスがそれ。

20160111_13

Flyweight

データベースのコネクションプーリングで使われている。

20160111_14

Proxy

いわゆる、アスペクト指向のパターンなのだが、C# の UI コントロールのメソッドが常に override と event を持っているのが proxy の役割になっている。あらかじめ仕込んでおかないといけないのがミソ。

20160111_15

Chain of Responsibility

複数のオブジェクトを連ねていくパターン。クリップボードのチェーンや、昔の常駐プログラムのキーチェーンで使われていた。いわゆる協調パターン。

20160111_16

Command

MVVM パターンにも出て来るおなじみの、ICommand インターフェースになる

20160111_17

Interpreter

いわゆる、式木だが、普通の人は使わない。動的にスクリプトを使うとか一部の関数型言語マニアが使う。F# だと簡単に作れる。

20160111_18

Iterator

いわゆる C++ のイテレーターだし、C# の IEnumerator だ。今だと、LINQ を使ったほうが楽。

20160111_20

Mediator

リモートメッセージで使われる SOAP とか JSON とかがそれにあたる。DCOM, CORBA を知っていればシリアライズという形で現れる。

20160111_23

Memento

いわゆる、Undo/Redo に使われるパターンなのだが、これを使った実装は見たことがない。そもそも Undo/Redo 自体が難しくて、今だとメモリのある限り丸ごと保存してしまう。例えば、Visual Studio をコンテナとして Undo/Redo メソッドとして利用する、ような感じ。

20160111_24

Observer

これも MVVM パターンで使われる ObservableCollection<T> の元ネタになる。

20160111_25

State

switch や if で実装しないために State パターンを使う。インターフェースを作っておくと、無限種類でオブジェクトを作れるので、State パターンは便利なのですよ。5 個程度ならば swtich、それ以上であれば State パターンを使えばよい。

20160111_26

Strategy

かつて openssl で使っていたパターンで、暗号コードの構造体を切り替える。ちなみに openssl は C言語だ。部分的に切り替えるのが State で、ごっそり切り替えるのが Strategy になる。ただし、.NET クラスライブラリに Strategy という単語はでてこなくて、Factory あたりでごっそりクラス名を切り替えたりする。ストリームクラスあたりがそれ。

20160111_27

Template Method

interface だったり、abstract だったりするが、言語仕様として Interface は内部フィールドを持たず、Abstract は内部データを持てる、という違いがあったりする。言語の都合なのと、IDE の補完機能を使って自動メソッドを使うときに便利なパターンだ。

20160111_28

Visitor

このあたりは、ほとんど delegate と一緒だったり、Action<>, Func<> で大丈夫だったりするが、異なるスレッドの場合はディスパッチが発生するため、このパターンが使われる?たぶん、C# だと Invoke になると思う。

20160111_29

 

 

改めて見直すと、コンテナとオブジェクトのパターンが多い。「パターン」自体が、頻出されるモデルという意味合いなので、かつてのオブジェクト指向以前であれこれと悩んだ末に出てきた定型パターンと考えられえる。定型だからこそ、現在の言語仕様やライブラリではそれらのパターンを取り入れてしまって、外側に出ないようにする。そういう意味では、GoF のパターンは形骸化しているといってよい。が、新しいプログラム環境で作ろうと思ったり、もっと大きい範囲でパターンを考える(ロボット制御の ROS とか、スマートフォンや PC などのメッシュ的な通信とか)ときには、これらのパターンが有効に働くので、知識として覚えておくとよいだろう。

おまけ

astah で作った GoF の図を置いてきます。

参考先

[amazonjs asin=”B000SEIBB8″ locale=”JP” title=”Design Patterns: Elements of Reusable Object-Oriented Software (Adobe Reader)”]

 

カテゴリー: 開発 パーマリンク