このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

ポートベース・コンポネント:補足

「ポートベース・コンポネント:ケーススタディ」への補足です。

名前を整理する

下の図、名前がかち合ってました。Pで目印したパーザー・コンポネントのparserポートの型がXionParser、最終的なプログラムの型もXionParser。



インターフェースを多用するようになると、名前が増えるのが悩みの種。醜悪なネーミングだと言われそうですが、明白にポートの型として定義したインターフェースの語尾にはPI(Port Interface)、コンポネントの実装クラスの語尾にはCmp(CoMPonent)を付けることにします。

それで:


public interface XionLexerPI {
public XionToken nextToken() throws IOException, XionLexException;
}


public interface XionParserPI {
public void parse() throws IOException, XionParseException, XionHandleException;
}

として、さらに:

  • XionLexerCmp は、レクサー・コンポネント(図のL)の実装クラス
  • XionParserCmp は、パーザー・コンポネント(図のP)の実装クラス

だと仮定します。

集約とラップ

上の図の「…x…」のところ、わかりにくいですね。レクサー・コンポネントとパーザー・コンポネントをlexerポートで繋いだ残りのポートinput, parser, handlerをまとめて外に突き出す箱が必要なら、次のように定義できます。


public class ParserProvider {
/* -- component profile */
@Provide
public XionParserPI parser;

@Require
public Reader input;
@Require(Necessity.OPTIONAL)
public XionExpressionHandler handler;
/* -- end profile */

public ParserProvider() {
// 生成
XionLexerCmp l = new XionLexerCmp();
XionParserCmp p = new XionParserCmp();
// 接続
p.lexer = l.lexer;
// ポートのセットアップ
this.input = l.input;
this.parser = p.parser;
this.handler = p.handler;
}
// ポートの集約が目的だから、メソッドはない。
}

ParserProvider x = new ParserProvider();とすれば、内部的にレクサーとパーザーが準備されて、ポートx.input, x.parser, x.handlerがすぐさま利用可能です。

しかし、上の図の状況では、わざわざParserProviderのようなものを定義する必要はなくて、最終的な目的であるXionParserのなかで集約も行えばいいでしょう。


public class XionParser {
/* 外部にポートを公開する必要はない。
* だが、ポートをちゃんと設定しておけば、
* ポートベースのスタイルで書けて気持ちよい。
*/
private XionParserPI parser;
private Reader input;
private XionExpressionHandler handler;

public XionParser() {
// 生成
XionLexerCmp l = new XionLexerCmp();
XionParserCmp p = new XionParserCmp();
// 接続
p.lexer = l.lexer;
// ポートのセットアップ
this.input = l.input;
this.parser = p.parser;
this.handler = p.handler;
}

/* インターフェースを互換とするためのグルーコード */
public void setExpressionHandler(XionExpressionHandler handler) {
this.handler = handler;
}
public void parse(Reader input) throws IOException,
XionParseException, XionHandleException {
this.input = input;
this.parser.parse();
}
}

このXionParserは互換性目的なのでポートを出していませんが、集約したコンポネントとしても使いたいなら、ParserProviderと同様に3本のポートを突き出しておいてもいいでしょう。

XionLoaderの場合も同様で、3つのコンポネント(昨日の図のL, P, B)を生成、適切に繋いで、残ったポートを集約し、それらのポートを使ってグルーコードを書けばいいのですね。実際に図示とコーディングをしてみると、レゴブロックを繋いでは積み上げるような感覚が味わえると思いますよ。