JavaScript

【まとめ】JavaScriptでデザインパターン

More than 3 years have passed since last update.

本記事は、『JavaScriptパターン』(O'Reilly)の第七章を読んで、JavaScriptでいろいろなデザインパターンを試してみようと思い書いた記事です。なお、各記事が長くなりそうなので分割しました。


リンク一覧


各デザインパターンについて


シングルトン

Javaのようなオブジェクト指向プログラミング言語では、静的と動的なプロパティ、メソッドが入り組んでいるためシングルトン作成は少し複雑でした。しかし、厳密なクラスを持たない動的な言語であるJavaScriptではシングルトンクラスの作成も理解も非常に簡単です。そもそも、JavaScriptにおけるオブジェクトをすべてシングルトンとみなすこともできるでしょう。要するに、 オブジェクトリテラルの作成 = シングルトンの作成 ともいえます。

ちなみに、もともとJavaでは、オブジェクトの生成(new)が非常に負荷のかかる処理だったため、使いまわしがきくオブジェクトを、毎回newするのではなく、オブジェクトの生成数を制限して使いまわそうとしたときの、有効なデザインパターンであったわけです。


ファクトリ

Factoryとは「工場」の意味ですよね。ファクトリの目的は、 「オブジェクト作成を容易にする」 ことです。工場で、ロボットみたいな製品が、雛形からどんどん製造されるイメージですね。具体的にいうと、オブジェクト生成時に文字列を受け取り、その文字列に合致する型のオブジェクトを返します。

ファクトリパターンで記述することによって、クラス生成のアルゴリズムは一か所に集約されます。それにしたがって、オブジェクトの型の追加や修正・メンテナンスが非常に簡単になります。


イテレータ

Iteratorパターンは、複雑なデータや特別な構造を持ったデータを持つオブジェクトに対して、簡単にアクセスできる方法を定義することです。JSONをパースするとか、xmlを扱うといったものも、今でこそネイティブにサポートされていたりベストプラクティスが確立されていたりするでしょうが、イテレータパターンといえるでしょう。

自分でサイトを開発する際に、たとえばほかのサイトやサービスとは汎用性がないけれども、そのサイトでは固有のデータ型(たとえば、決まった形のオブジェクトの入れ子)があったとします。そうしたデータにアクセス(取り出したり、書き換えたり、削除したり)するためのユーティリティメソッドのようなものを自分で書く、となったときに、それは一種のイテレータパターンに準拠して書いていることになります。


デコレータ

Decoratorパターンでは、基本となるオブジェクトを、基本オブジェクトと同じインターフェースを持つデコレータオブジェクトでラップすることで、動的にオブジェクトの機能を追加することができます。ようするに、オブジェクトという名のクリスマスツリーをを新しい機能という飾り物ででどんどん「装飾(decorate)」することができますよ、ということです。

具体的には、「money」という基本オブジェクトに対して、「税額追加」「単位変更」といった機能があるといった例。また、「car」という基本オブジェクトに対して、「エンジン変更」「色変更」「窓変更」といった機能がある、といった例などがあるでしょう。あらかじめ定義されたデコレータオブジェクトが提供する機能を追加することで、オブジェクトを実行時に調整します。


ストラテジー

Strategyパターンは、同じインターフェイスを維持しつつ、特定の作業を処理する際に、いくつかの利用可能なアルゴリズムの中から特定のアルゴリズムを選択して利用する、といったパターンに使えます。実行時に文字列の引数などで実行するコンテクストのアルゴリズムを選択するわけです。

具体的には、バリデーションの機能(文字列が空?数値かどうか?)や、暗号化の機能(SHA-128? SHA-256? SHA-512?)といった場合に利用できるでしょう。


ファサード

Facadeとは「玄関」の意味です。Facadeパターンを一言でいうと、複雑なメソッド群に対するwrapperです。つまり、オブジェクトにだいたいのインターフェースを提供し、あまり多くの作業を詰め込まないようにするためのデザインプラクティスです。要するに、メソッド呼び出しをまとめた新しいメソッドを定義するということです。

プログラムを作っていると、最初は小さなものでもだんだん大きくなっていきますよね。メソッド間の相互関連も複雑になってきます。それぞれのメソッドを使うには、メソッドの機能を正しく理解し、正しい引数のもと、正しい順番に呼び出す必要があります。Facadeパターンでは、その処理を行うための「玄関」、つまり処理のための窓口・入口を準備しておくというパターンになります。

具体的には、メソッドとしてはpreventDefault()stopPropagationも別々ですが、この二つのメソッドを同じタイミングで呼び出すことがほとんどなので、たとえばstop()のような、この二つのメソッドを呼び出すためだけの新しいwrapperメソッドを定義する、といった例などがあげられます。


プロキシ

Proxyとは「代理」や「委任」の意味。そもそも「プロキシサーバ」という語から、たいていのエンジニアにはなじみの深い言葉ですね。Facadeパターンでは単純にメソッド呼び出しのためのメソッドを定義しましたが、Proxyパターンではもう少し複雑なことをします。

『JavaScriptパターン』(O'Reilly)からそのまま引用するなら、「オブジェクトを包んでオブジェクトへのアクセスを管理します。操作をグループにまとめるか、あるいはほんとうに必要な時だけ操作を実行することによって、コストが高い操作を避けるのが目的です。」(p.187)ということです。

具体的には、遅延初期化などに使われます。遅延初期化とは、初期化の際にコストのかかる作業がある場合、クライアントがそれを初期化の際にすべて実行していたら負担がかかってしまうような場合、プロキシが中継し、遅れて作業を実行する、といったような例があげられます。

プロキシを必要とする理由は以下の通りです。(引用元)


  • 本当に必要になるまで、大きなオブジェクトのインスタンス化を遅らせる

  • リモートのオリジナルオブジェクトにアクセスを提供する

  • オリジナルオブジェクトに対するアクセスを制御する


メディエータ

Mediatorは「仲介者、まとめ役、橋渡し役」の意味です。オブジェクト間の保守性を担保するために、オブジェクト相互の直接的なやり取りを仲介することによって、疎結合を促進します。

疎結合の対義語である密結合とは、コードが肥大化し、オブジェクトやメソッドがお互いに関連しすぎ合っていて保守しにくくなっている状態(おしくらまんじゅうみたいですね)を表します。疎結合を促進することによって、変更も保守もしやすくなります。

具体的には、プロキシを使ってHTTPリクエストをコントロール(束ねて一回のリクエストにして送る等)する例などがあります。


オブザーバ

Observerは「傍観者、観察者」の意味ですね。DOM操作をするJavaScriptなどでは、たとえば、マウスオーバーやクリックといったブラウザイベントに対して、ポップアップを表示させる、アニメーションを発動させる、といった処理を定義できます。

DOM操作によるブラウザイベント関わらず、メッセージの送信、ログイン、そういったあらゆるイベントを含めたカスタムイベントを「観察」し、それらが発生したときにオブザーバに通知するようなパターン、それがObserverパターンです。なお、観察しているObserverに対して、観察されているオブジェクトはSubjectと呼ばれます。

SPAとかとなると、カスタムイベントを自分で数種類定義して、それに対する処理をコントロールする、といったことを普通にやりますので、デザインパターンとして理解しておくとそこらへんのコーディングがはかどるかと思います。


コンポジット

Compositeとは「複合、混合」といった意味です。「枝」、「葉」といった概念で説明されることが多いですが、要素であるオブジェクト(単独要素、リーフ)と、単独要素を複数含んでいる複合オブジェクト(複合要素、コンポジット)それぞれを区別なく扱えるという特徴を持っています。

これは、ディレクトリ(複合要素)とファイル(単独要素)を構成する場合によく使われるデザインパターンです。また、売り上げの集計システムや、ギャラリーと画像の関連性を記述する際などで使われることもあるようです。


参考リンク