まめーじぇんと@Tech

技術ネタに関して (Android, GAE, Angular). Twitter: @mame01122

Javascriptでデザインパターン (その8: Decorator)

今回はDecoratorパターン。

8回目は、Decoratorパターン。compositeパターンとよく似て、中身と外側(今回は中身と装飾)を同一視することで、使う側が中身を意識する必要がなくなるメリットがあります。

そもそもDecoratorパターンは?

Wikipediaによれば、"このパターンは、既存のオブジェクトに新しい機能や振る舞いを動的に追加することを可能にする"とのこと。

僕の言葉でいえば、
・核となるオブジェクトがあってそれに微妙な変更を頻繁に/動的に加えたいときに使う
・(これはクラスベースの言語特有の説明かもですが)通常核となるオブジェクトがある場合は、継承(extend)することで機能拡張をするが、そのパターンが多いときは"継承の継承の継承・・・"というように、そのパターンがネズミ算的に増えてしまう。
・そこでDecoratorパターンでは、その"微妙な変更"を1つのクラスとして切り出して上記のようなパターンを減らすことを目的とするですかね。

サンプルコード

サンプルコードです。
何か実践に近い例はないかなぁ・・・と探した結果、Photoshopのような画像編集ソフトの例を思いつきました。
Photoshopのような編集ソフトは、効果として、元の画像に何回も/何種類もエフェクトを追加すると思います。そのエフェクトの管理は、このDecoratorパターンで実現できますね。

下記の例では、Contentという元々の画像に対して、"ColorVisualEffect", "AlphaVisualEffect"を加える例です。(本当はもっと多く書いてもいいのですが、コードが長くなってしまうので2つだけに絞っています)

まずは、核となる元画像(Content)を作ります。

// Component
var Content = function(){
};

Content.prototype = {
	show : function(){
		console.log("Original image");
	}
};

特に変わったところはないですね。show()は、画像を表示するメソッドです。

続いて、ColorVisualEffectを定義します。

var ColorVisualEffect = function(contentArg, paramArg){
	this.content = contentArg;
	this.param = paramArg;
};

ColorVisualEffect.prototype = {
	show : function(){
		this.content.show();
		console.log("Color value: " + this.param);
	}
};

ここでのキモは、"show()"のメソッドがContentと同じであること(中身とエフェクトを同一視している)、そしてContentをメンバ変数として持つことです。メンバ変数として持つ理由は、後ほど。

続いて、AlphaVisualEffect。これはColorVisualEffectとほぼ同じなので、特に説明はいらないかと思います。

var AlphaVisualEffect = function(contentArg, paramArg){
	this.content = contentArg;
	this.param = paramArg;
};

AlphaVisualEffect.prototype = {
	show : function(param){
		this.content.show();
		console.log("Alpha value: " + this.param);
	}
};

そして、これを実際に表示してみます。まずは、元画像のみ。

var result1 = new Content();
result1.show();

結果。

Original image

そりゃそうだ。

続いて、これにColorVisualEffectを追加します。
ここで、ColorVisualEffectのコンストラクタにContentを渡してメンバ変数に保持している部分が効果を発揮します。ColorVisualEffectのshow()が呼ばれると、Contentのshow()も呼ばれるようになっています。

var result2 = new ColorVisualEffect(new Content(), 50);
result2.show();

その結果。

Original image
Color value: 50

ちゃんと元画像と、ColorVisualEffectも適用されています(値の50というのは適当です)

最後に、ColorVisualEffectをかけつつ、AlphaVisualEffectもかけてみます。それぞれのコンストラクタに同じContentの型を渡しているおかげで、つなげてエフェクトを適用することが可能になっています。

var result3 = new AlphaVisualEffect(new ColorVisualEffect(new Content(), 50), 25);
result3.show();

結果。

Original image
Color value: 50
Alpha value: 25

ColorVisualEffectもAlhpaVisualEffectも適用されましたね。

このパターンは、もちろん同じインスタンスを2回作成すれば、同じエフェクトを2回適用することも可能です。

new AlphaVisualEffect(new AlphaVisualEffect(new Content(), 80), 45);

とやると、透明度を80%にした後に、さらに45%にする・・・という処理になります。

余談・・・

Photoshopでエフェクトを重ねがけしようとすると、”新しいインスタンスが作成されます”という感じの注意がなされますが、これって、おそらくDecoratorパターンを使って、新しいエフェクトのインスタンスを作ろうとしているんじゃないかと思っています。もちろん、Photoshopソースコードなんて見たことがないので推測ですが。

以上、Decoratorパターンでした!

Javascriptでデザインパターン (その7: Chain of Responsibility)

8回目の今回は、Chain of responsibilityパターン。

日本語に訳すると”責任のたらい回し”。訳すると、響きはかなり悪いです(笑)

Chain of Responsibilityとは?

Wikipediaによると、"一つの コマンドオブジェクトと一連の 処理オブジェクトから構成される。各処理オブジェクトは、処理できるコマンドオブジェクトの種類と、自身が処理できないコマンドオブジェクトをチェーン内の次の処理オブジェクトに渡す方法を記述する情報を保持する。また、新たな処理オブジェクトをチェーンの最後に追加する機構を備える。"とのこと。

僕の言葉で説明すると、
・ソフトウェアを作る上でいろいろな条件分岐をすることがあるけど、あまりにその条件が増えると見にくくなるよね?
・ので、それをオブジェクトのチェーンで表現するようにして、使う側がその分岐を意識する必要がなくなるようにするよ
ですかね。

下記のように、入力された文字のバリデーションに使うこともできるかとは思います。が、最近のJavaScriptのライブラリはそのあたりも充実していたりするので、あまりがんばっても車輪の再発明かなぁと思ったり思わなかったり。。。

今回のサンプルコード

今回はユーザ入力する文字列に対して、Null / 最小の文字数 / 最大の文字数を確認するためのValidatorを作りたいと思います。このテのチェックはプロジェクト後半にも追加になったりしてその都度if - elseのネストが深くなるので、このChain of responsibilityを使えばそれを防ぐことができます。

まずは、最小、最大の文字数を定義します。これは普通ですね。

var MIN_LENGTH = 4;
var MAX_LENGTH =8;

続いて、確認するポイントごとにチェーンを作っていきます。まずはNullチェック。コンストラクタで定義しているthis.nextは、チェーンでつながる先です。setNext()メソッドの引数として渡され、各インスタンスがつながる先を持つことになります。

request()の引数には対象となる文字が渡され、nullの場合は、その旨を表示してチェーンを終了します。もしnullでない場合は、次のチェックに写るため、自分が持つthis.nextのrequest()を呼びます。

var NullCheckHandler = function(){
	this.next= null;
	console.log("NullCheckHandler constructor");
};

NullCheckHandler.prototype = {

	setNext : function(chain){
		console.log("NullCheckHandler setNext");
		this.next = chain;
		return this;
	},

	request : function(input){
		console.log("NullCheckHandler request");
		if(input === null){
			console.log("Input is null, End of chain");
		} else {
			if(this.next !== null){
				console.log("Go to next chain");
				this.next.request(input);
			} else {
				console.log("End of chain");
			}
		}
	}
};

ここからはほぼ繰り返し。最低の文字列以上かを確認しています。

var MinLengthHandler = function(){
	this.next= null;
	console.log("MinLengthHandler constructor");
};

MinLengthHandler.prototype = {

	setNext : function(chain){
		console.log("MinLengthHandler setNext");
		this.next = chain;
		return this;
	},

	request : function(input){
		console.log("MinLengthHandler request");
		if(input.length < MIN_LENGTH){
			console.log("Input is less than minimum length, End of chain");
		} else {
			if(this.next !== null){
				console.log("Go to next chain");
				this.next.request(input);
			} else {
				console.log("End of chain");
			}
		}
	}
};

最大文字数以下であることを確認。もし最大文字数以下(すべてのチェックが問題ない場合)は、その旨を表示して終了します。

var MaxLengthHandler = function(){
	this.next= null;
	console.log("MaxLengthHandler constructor");
};

MaxLengthHandler.prototype = {

	setNext : function(chain){
		console.log("MaxLengthHandler setNext");
		this.next = chain;
		return this;
	},

	request : function(input){
		console.log("MaxLengthHandler request");
		if(input.length > MAX_LENGTH){
			console.log("Input is more than minimum length, End of chain");
		} else {
			if(this.next !== null){
				console.log("Go to next chain");
				this.next.request(input);
			} else {
				console.log("End of chain");
			}
		}

	}
};

そして、上記のValidatorを確認するためのClientの定義です。

チェックする項目ごとにインスタンスを生成し、それをsetNextでつなげます。Javaであれば一続きで書くことができるのですが、Javascriptは同じプロトタイプベースで同じクラスを継承していないため、一つずつ別の行に書く必要があります。ここ、ちょっとカッコ悪い。

var Client = function(){
	var nullCheckHandler = new NullCheckHandler();
	var minLengthHandler = new MinLengthHandler();
	var maxLengthHandler = new MaxLengthHandler();
	nullCheckHandler.setNext(minLengthHandler);
	minLengthHandler.setNext(maxLengthHandler);

作ったチェーンを、今回確認したいユースケースごとに呼び出します。Nullと、最小文字数以下、最大文字数以上の3つのケースですね。ここは特に説明する部分はないかと思います。

	console.log(" --- Check null ---");
	var wordA = null;
	nullCheckHandler.request(wordA);

	console.log(" --- Check less than minimum lenght string ---");
	var wordB = "123";
	nullCheckHandler.request(wordB);

	console.log(" --- Check more than minimum lenght string ---");
	var wordC = "1234567890";
	nullCheckHandler.request(wordC);
};

最後にclientを呼び出します。

var client = new Client();

その結果、ログはこんな感じで表示されます!

NullCheckHandler constructor
MinLengthHandler constructor
MaxLengthHandler constructor
NullCheckHandler setNext
MinLengthHandler setNext
 --- Check null ---
NullCheckHandler request
Input is null, End of chain
 --- Check less than minimum lenght string ---
NullCheckHandler request
Go to next chain
MinLengthHandler request
Input is less than minimum length, End of chain
 --- Check more than minimum lenght string ---
NullCheckHandler request
Go to next chain
MinLengthHandler request
Go to next chain
MaxLengthHandler request
Input is more than minimum length, End of chain

Chain of Responsibilityは以上です!

Javascriptでデザインパターン (その7: Iterator)

イテレータパターンって?

Wikipediaによれば、”コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。”とのこと。この説明は比較的分かりやすいかもですね。

僕の言葉で説明すれば、
・配列やListなどの要素をループで1つずつ取得、処理を行いたいときに使う。
・その際、配列やListを通常のfor文でまわすと、その配列やListと使う側との結合が密になりすぎてしまう。つまり、配列やListを異なる管理方法にした場合(Vectorにするとか)、それを使う側にも影響が及んでしまう。
・そこで、Iteratorというインタフェースを間に挟むことにより、使う側からは、使いたいデータの構造が何なのかを知ることなく使えるようにする(疎結合にする)

です。

これまでIteratorを使ってカウントすることはあっても自分で実装することはなかったので、今回、一度Javaで書いてからJavascriptにポーティングしてみました(Javaのコードは載せてませんが)

サンプルコード

では実際のコードです。まずはIteratorを定義します。使う側は、このIteratorを使うことで、実際のデータ構造を意識する必要がなくなります。
hasNext()は次の要素があればtrue、なければfalseを返す関数です。使う側がwhile文でループをまわすことを想定しています。

next()は現在のインデックスの要素を返して、インデックスを1つ進めるメソッドです。そのままですね。
コンストラクタで出てくるaggregateは後ほど説明します。

// Iterator + Concrete Iterator
var Iterator =  function(arg){

	this.index = 0;
	this.aggregate = arg;
};

Iterator.prototype = {
	hasNext : function(){
		if(this.aggregate.getSize() > this.index){
			return true;
		} else {
			return false;
		}
	},

	next : function(){
		var item = this.aggregate.getItemAt(this.index);
		this.index = this.index + 1;
		return item;
	}
};

続いてAggregateを定義します。

Aggregateに実際のデータが格納されています。今回だと、Elementが該当します。

そしてメソッドは、必須となるのがiterator()です。使う側はこのメソッドを呼ぶことによりイテレータインスタンスを取得します。その他のadd(), getSize(), getItemAt()は任意です。ユースケースによっては、例えば逆順で数えたりするインタフェースを追加してもいいかと思います。

// Aggregate + Concrete Aggregate
var Aggregate = function(){
	this.Element = [];
};

Aggregate.prototype = {
	iterator : function(){
		return new Iterator(this);
	},

	add : function(item){
		this.Element.push(item);
	},

	getSize : function(){
		return this.Element.length;
	},

	getItemAt : function(index){
		return this.Element[index];
	}

};

最後にこれまで定義したIteratorとAggregateを使う部分です。aggregateにaddしているのは準備部分です。その下でaggregateからiteratorインスタンスを取得し、ループをまわしてデータを取得します。

var aggregate= new Aggregate();
aggregate.add("Item A");
aggregate.add("Item B");
aggregate.add("Item C");
aggregate.add("Item D");
aggregate.add("Item E");

var iterator = aggregate.iterator();
while(iterator.hasNext()){
	var item = iterator.next();
	console.log("item: " + item);
}

実行結果は、

item: Item A
item: Item B
item: Item C
item: Item D
item: Item E

となっており、問題なく動いていることが確認できます。

データ構造が変わった場合は?

今回の例だと、aggregateの中身が変わることになります。
ここのデータ構造がかわってもaggregate内での修正(と、場合によってはIteratorも修正)に閉じるので、使う側は呼び方を変える必要がありません。


今回はJavascriptというより、イテレータのパターンの勉強になりました。

Javascriptでデザインパターン (その6: Strategy)

今回はStrategyパターン。

今回で6つ目のデザインパターンです。使ったことのあるデザインパターンを大分出しつくした感があります・・・。苦笑。

そもそもStrategyパターンとは?

wikipediaによると、"アルゴリズムを実行時に選択することができるデザインパターンである"とのこと。何だか分かるような分からないような。

僕の言葉だと、
・例えばHTTPとHTTPSなど、外から見ると同じインタフェースだけど中身のロジックを切り替える必要のある部分で、柔軟にロジックを切り替えることを可能とするパターン”です。
このパターンは仕様変更があることを前提とした(もしくは不確定なまま進む)アジャイルのプロセスと相性がいいかもですね(実際にそこまで使ったことはない。。)
です。

サンプルコードです。

今回の例は、デザイナ (Designerインスタンス)が仕事中、HTMLで書くべきかプレーンテキストでかくべきか決まってない、というケースだとします。このデザイナさんはHTMLとプレーンテキスト、どちらでも素早く納品できるように(下記の例だとshow()できるように)Strategyパターンを使います。

下記の例だと、Designerのインスタンスを作る時に渡される"HtmlStrategy"と"PlainTextStrategy"に応じて、最終的に吐き出されるアウトプットが異なります。Designerへの引数を変えるだけで内部のロジックが変更されるのが伝わるでしょうか。

キモは、HtmlStrategyとPlainTextStrategyが持つ"show()"というメソッド名が同じ、というところですね。ここが同じなため、各Strategyを使うDesignerは"show()"を知っているだけで済みます。

// Concrete Strategy 1
var HtmlStrategy = function(arg) {
	this.content = arg;
};

HtmlStrategy.prototype = {
	show : function(){
		console.log("<html> " + this.content + "</html>");
	}
};

// Concrete Strategy 2
var PlainTextStrategy = function(arg) {
	this.content = arg;
};

PlainTextStrategy.prototype = {
	show : function(){
		console.log("plain text: " + this.content);
	}
};

// Context
var Designer = function(direction){
	this.Strategy = direction;
};

Designer.prototype = {
	createOutput : function(){
		this.Strategy.show();
	}
};

var htmlDesigner = new Designer(new HtmlStrategy("Create html"));
htmlDesigner.createOutput(); // <html> Create html</html>

var textEditor = new Designer(new PlainTextStrategy("Write plain text down"));
textEditor.createOutput(); // plain text:Write plain text down.

Strategyパターンは以上です!(他のウェブサイトのコードも全く見てないけど、問題ないハズ!)

Javascriptでデザインパターン (その5: Builder)

今回はBuilderパターン。

このシリーズも5回目まで来ました。結構いい感じのペースで更新できてる気がします。今回はBuilderパターン。このパターンは、Androidとかを書いてると結構いろんなところで見ますね。DialogBuilderとか。
今回は、そのBuilderパターンをJavascriptで書いてみようと思います。

そもそもBuilderパターンとは?

wikipediaによれば、" オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にする"とのこと。・・・何のこっちゃですね。

僕の言葉で説明すれば、”何か多くの引数を必要とするインスタンスを作るときに、コンストラクタで全部渡してもいいんだけど、10個も20個も引数に並べると、順番を注意深く確認したりして実装も大変だよね?ということで、必要なものをコンストラクタの引数ではなく、必要なものだけsetできるようにするよ。これだと開発途中に引数が増えてもコンストラクタを修正→そのコンストラクタを使っている他のクラスも一緒に修正という事態を防ぐことができるよ”ですかね。

このデザインパターンが単純なsetterと異なるのは、メソッドチェーンが使えること。setterだけだと、

object.setParamA("hoge");
object.setParamA("foo");
object.setParamA("bar");
.
.

というように必要な数だけsetterを呼ばないといけませんが、Builderパターンを使えば、

object.setParamA("hoge").setParamA("foo").setParamA("bar")...;

というようにシンプルに書くことができます。

またBuilderパターンには、Builderから生成されるインスタンスに余計なsetter系のメソッドを追加しなくて済む、というメリットもあります。

インスタンスに余計なsetterがついていると、(特に大規模開発では)他のプログラマにそれだけ意図せぬコーディングがなされる可能性が出てきてしまいます。”いやー、そこで値をsetしちゃう?”みたいな。

サンプルコード

では、実際の中身です。今回は、家を作ることにしました。家は、ドアと、壁と、窓が3枚と、煙突を持つことにします。家はデフォルトのオプションで見た目が決まっていますが、Builderパターンを使ってそれらを自由にカスタマイズする、ということにしましょう。

まずは、家を定義します。

// Product
var House = function(argDoor, argWall, argWindowA, argWindowB, argWindowC, argChimney){
	this.door = argDoor;
	this.wall = argWall;
	this.windowA = argWindowA;
	this.windowB = argWindowB;
	this.windowC = argWindowC;
	this.chimney = argChimney;
};

House.prototype = {

	getDoor : function(){
		return this.door;
	},

	getWall : function(){
		return this.wall;
	},
	getWindowA : function(){
		return this.windowA;
	},
	getWindowB : function(){
		return this.windowB;
	},
	getWindowC : function(){
		return this.windowC;
	},
	getChimney : function(){
		return this.chimney;
	}
};


キモは、家のインスタンスにsetter系のメソッドがないこと、です(理由は上記のとおり)

続いて、Builderを定義します。コード自体は多少長いですが、同じような内容が続くのでそんなに見づらくはないかと思います。

// Builder
var HouseBuilder = function(){
	this.house = new House("my Door", "my wall", "my window A", "my window B", "my window C", "my chimney");
};

// Concrete Builder
HouseBuilder.prototype = {

	setDoor : function(door){
		console.log("setDoor");
		this.house.door = door;
		return this;
	},

	setWall : function(wall){
		console.log("setWall");
		this.house.wall = wall;
		return this;
	},

	setWindowA: function(windowA){
		console.log("setWindowA");
		this.house.windowA = windowA;
		return this;
	},

	setWindowB: function(windowB){
		console.log("setWindowB");
		this.house.windowB = windowB;
		return this;
	},

	setWindowC: function(windowC){
		console.log("setWindowC");
		this.house.windowC = windowC;
		return this;
	},

	setChimney : function(chimney){
		console.log("setChimney");
		this.house.chimney = chimney;
		return this;
	},

	getConstructedInstance : function(){
		console.log("getConstructedInstance");
		var tmp = this.house;
		return tmp.getDoor() + " / " + tmp.getWall() + " / " + tmp.getWindowA() + " / " + tmp.getWindowB() + " / " + tmp.getWindowC() + " / " + tmp.getChimney();
	}
};

ここでのキモは、HouseBuilder.prototype内のsetter系のメソッドが、すべて自分自身(this)を返している部分です。ここでthisを返すことにより、メソッドチェーンを実現することができます。

最後に、これを呼び出す部分(BuilderパターンではDirectorと呼ばれる)を定義します。

// Director
var builder = new HouseBuilder();
var result = builder.setDoor("My Brown door").setWall("My white wall").setWindowA("My great window A").setWindowB("My miracle window B").setWindowC("My super window C").setChimney("My big chimney").getConstructedInstance();
console.log("result: " + result);

見てわかるように、builderのインスタンスを作った後、自分好みにsetXXXを呼び出してカスタマイズしています。その後、getConstructedInstance()を呼び出して出来上がったインスタンス(ここでは文字列にしてしまっていますが)を取得しています。

以上でBuilderパターンは終了です!

Javascriptでデザインパターン (その4: Prototype)

続いてPrototypeパターン。

これで4つ目。

Singleton, Factory, Mediatorに関しては下記参照。

Singleton: http://mame0112.hatenablog.com/entry/2015/07/26/130425
Factory: http://mame0112.hatenablog.com/entry/2015/07/26/195821
Mediator: http://mame0112.hatenablog.com/entry/2015/07/26/210249

Prototypeパターンって?

同じようなインスタンス複数作るときに使うデザインパターンです。Javaではいまだにそんなユースケースに出会ったことないですが。。。
Javascriptの継承で使う"prototype"と、このデザインパターンの"Prototype"は同じ意味合い(日本語訳だと、”試作品”よりは”原型”ですね)のようです。

プログラマ的なメリットは、同じようなインスタンスを作るときに、コンストラクタの引数を少しずつ変えて作る必要がなくなり、テンプレートとなるインスタンスから作れて工数削減やケアレスミス防止につながる部分ですね。

また、”同じようなインスタンスを作る”という意味では、Builderパターンにも近いですね。Builderパターンではイチからインスタンスを作る場合と、(例えば)createFrom()というメソッドの引数に原型となるインスタンスを渡してそのインスタンスに対してsetしていく、という使い方もしたりするので。話がそれました。

サンプルコード

そんなに説明することもないので、早速サンプルコード。

var Prototype = function(){

	//Template values
	this.id = 0;
	this.name = "test name";
	this.property1 = "prop1";
	this.property2 = "prop2";

};

Prototype.prototype = {
	clone : function(){
		console.log("clone");
		return new Prototype(this.id, this.name, this.property1, this.property2);
	},

	setId: function(id){
		this.id = id;
	},

	setName: function(name){
		this.name = name;
	},

	getResult: function(){
		console.log("id: " + this.id + " name: " + this.name + " property1: " + this.property1 + " property2: " + this.property2);
	}
};

var typeA = new Prototype();
typeA.getResult(); // id: 0 name: test name property1: prop1 property2: prop2

var typeB = typeA.clone();
typeB.setId(2);
typeB.setName("test name2");

typeA.getResult(); // id: 0 name: test name property1: prop1 property2: prop2
typeB.getResult(); // id: 2 name: test name2 property1: prop1 property2: prop2

キモは、cloneメソッド内でインスタンスを作っているところですかね。ここで新しいインスタンスを作らないと、同じ値を参照してしまい、意図せぬ挙動になります。また、最近(??はnewの代わりにObject.create()を使うべき(すでにnewを使うのはアンチパターンとか・・・?)という話もあるので、この部分は今後調べたみようと思います(この部分も結構奥が深い・・・)

また、(他のパターンも然りですが)このデザインパターンJavascriptではいろんな書き方ができそうですね。ので、今回のはあくまで一例ということで。

Javascriptでデザインパターン (その3: Mediator)

今回は、Mediatorパターン

3回目の今回は、Mediatorパターン。仲介者パターンです。

そもそもこのパターンは、Wikipediaによれば、
"オブジェクト間の通信は mediator によってカプセル化され、オブジェクト同士で直接通信せず、mediator を介して行うようになる。これにより通信するオブジェクト同士の依存関係を削減し、結合の度合いを下げることができる。"とのこと。

僕の言葉で書くと、”複雑なアプリやサービスの(特にコントローラまわりのクラスで)多数のクラスやアクションを管理する際、管理する際のロジックが複雑になることがあり(大量のif - elseやswitch文など)それが保守性と可読性を下げるため、そのようなクラスやアクションを管理するための仲介者をもうけるデザインパターン。クラスやアクション間の挙動に関しては、一律この仲介者(Mediator)を介してやりとりすることにより、クラス間の依存やメンテコストを下げることが可能となる。
※こうやって書くと、いかにWikipediaの言葉が凝縮されて短文になっていることか・・・まあ、そのおかげで抽象的になって分かりにくいこともあるんですが。

これまでMediatorパターンは、あまり実際のコードでみたことがないのですが(単純なものであれば、FactoryパターンやStrategyパターンで作れてしまう)、今回勉強ということで、JavascriptでMediatorパターンを書いてみました(ので、JavaUMLを見つつですが・・・)

実際に登場する役割

・Mediator: Colleagueとのインタフェースを定義する。
・ConcreteMediator: Colleagueを管理する。Colleague(=上記の説明であれば、クラスやアクション)に応じたロジックを持つのはここ。
・Colleague: 管理されるクラスやアクション。
といったところですかね。

実際のソースコードはこちら。

// Mediator
var Mediator = function (){
	this.colleagues = {};
};

// Concrete Mediator

Mediator.prototype = {
	addColleague : function(name, colleague){
		console.log("addColleague");
		this.colleagues[name] = colleague;
	},

	consultation : function(name){
		console.log("consultation: " + name);
		var prop = this.colleagues[name];
		prop.advice("This is advice");
		// colleagues.advice(collegague);
	}
};

var Colleague = function(){
	var mediator;
};

Colleague.prototype = {
	setMediator : function(newMediator){
		console.log("setMediator");
		mediator = newMediator;
	},

	advice : function(content){
		console.log("I got advice: " + content);
	}
};

var concreteMediator = new Mediator();

var colleagueA = new Colleague();
colleagueA.setMediator(concreteMediator);
concreteMediator.addColleague("colleagueA", colleagueA);
var colleagueB = new Colleague();
colleagueB.setMediator(concreteMediator);
concreteMediator.addColleague("colleagueB", colleagueB);

concreteMediator.consultation("colleagueA");
concreteMediator.consultation("colleagueB");

少しずつ、Javascriptに慣れてきた気がします。