Typescriptでデザインパターンをやる。
どうせなのでGoF網羅しよう。interpreter patternはちょっと無理かもしれませんが。
Adapter Patternとは?
Adapter パターン
Adapter パターンを用いると、既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。Adapter パターンを実現するための手法として継承を利用した手法と委譲を利用した手法が存在する。
だそうです。
Adapter Patternを実装するには?
Adapter Patternはinterfaceがあればやれるはず、Typescriptにinterfaceはあるか?
ある。
interface Logger{ log(msg: string): void; }
こういう感じで書ける。interfaceはtscでコンパイルする時にだけ使われる。
interfaceをimplementsしているclassがintefaceを満たすかチェックしたり、interfaceを取り扱っている部分での型チェックなどを行う。コンパイル後のjsにはinterfaceは出力されない。javascriptにはinterfaceに準ずる機能はないので当然かもしれない。
このintefaceを使えばAdapter Patternを実装する事が出来そうだ。
実装
javascriptでログ出力を行う方法はパッと以下の3つが考えられる。
それぞれで出力の仕方が異なるのでAdapter Patternを使ってログ出力のインタフェースを統一する。
実装は以下。intefaceとしてLoggerを定義し、それぞれの出力方法のAdapterを作る。ぞれぞれのLoggerはLogAdapterFactoryを通して受け取る。LogAdapterFactoryにはLOGGER_TYPEを渡す。Typescriptにenumはないので擬似でLOGGER_TYPEというのを作った。内部的には唯のstringなのでちょっとあれだけど知らん。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** A unified logging interface */ | |
interface Logger{ | |
log(msg: string): void; | |
} | |
/** Factory for enums */ | |
class LOGGER_TYPE{ | |
static CONSOLE:string="CONSOLE"; | |
static ALERT:string="ALERT"; | |
static DISPLAY:string="DISPLAY"; | |
} | |
class LogAdapterFactory{ | |
static createLogger(type:string): Logger{ | |
switch(type){ | |
case LOGGER_TYPE.CONSOLE: | |
return new ConsoleLogger(); | |
case LOGGER_TYPE.ALERT: | |
return new AlertLogger(); | |
case LOGGER_TYPE.DISPLAY: | |
return new DisplayLogger(); | |
} | |
return null; | |
} | |
} | |
class ConsoleLogger implements Logger{ | |
log(msg:string){ | |
console.log(msg); | |
} | |
} | |
class AlertLogger implements Logger{ | |
log(msg:string){ | |
alert(msg); | |
} | |
} | |
class DisplayLogger implements Logger{ | |
display:HTMLDivElement; | |
count:number; | |
constructor(){ | |
this.count = 0; | |
this.display = <HTMLDivElement>document.createElement("div"); | |
this.display.style.position="absolute"; | |
this.display.style.top="0px"; | |
this.display.style.right="0px"; | |
this.display.style.width="200px"; | |
this.display.style.background="#ddffdd"; | |
this.display.style.padding="10px"; | |
this.display.style.fontSize="10px"; | |
document.body.appendChild(this.display); | |
} | |
log(msg:string){ | |
if(this.count > 10){ | |
this.display.removeChild(this.display.childNodes[0]); | |
} | |
else{ | |
this.count++; | |
} | |
this.display.innerHTML += "<div>"+msg+"</div>"; | |
} | |
} | |
使う
Loggerを使ってみる。LogAdapterFactory.createLogger(LOGGER_TYPE.DISPLAY);のLOGGER_TYPEを変えればそれぞれの出力に切り替わる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script type="text/javascript" src="dp.js"></script> | |
<script type="text/javascript"> | |
var logger; | |
window.onload = function(){ | |
logger = LogAdapterFactory.createLogger(LOGGER_TYPE.DISPLAY); | |
logger.log("output log"); | |
} | |
</script> | |
</head> | |
<body> | |
<input type="text" value="write log here." id="log" /> | |
<input type="button" value="出力" onclick="logger.log(document.getElementById('log').value)" /> | |
</body> | |
</html> |
終わりに
javascriptでこれを実現するコードを書くのは別に難しくない、けど、今までそういう事をしようとした場合「js的にこうでいいかなぁ?」と毎回悩んでいた。Typescriptだとこの辺りをすっ飛ばして設計に専念しやすいと感じる。
0 件のコメント:
コメントを投稿