ユーティリティの構造【編集中】

BIG-Modelアプリケーションでは機能ごとにユーティリティファイルにまとめています。

ここではユーティリティファイルの構造について説明します。

ユーティリティの中身

ユーティリティファイルの中身は機能の性質にもよりますが、おおむね次のようなもので構成しています。

(1) フロントエンド処理
(2) バックエンド処理
(3) イベントフック処理(任意)

フロントエンド処理

パソコン通信システムで表示するメニューやメッセージ、プロンプトなど、利用者が目にするため(例えば視覚的など)の処理を行います。

BIG-Modelアプリケーションではフロントエンド処理のコード定義を、時系列で定義または処理が行えるよう考慮していて、BASICやDOSのバッチファイルのようなコード定義とほぼ同じです。

例えば、ユーティリティの処理の工程が大きく3つあるなら、

[1] 第1工程
[2] 第2工程
[3] 第3工程

のように定義しておき、上から順番に処理するような仕組みです。

具体的には、ハッシュ変数subsに各工程のコードを定義し、BASICの行番号のように採番します。

my $subs = {
  '1' => sub {
    ~~第1工程の処理~~
  }, 
  '2' => sub {
    ~~第2工程の処理~~
  }, 
  '3' => sub {
    ~~第3工程の処理~~
  }, 
}

そして各コードを呼び出すための関数call()を用意し、呼び出しの際、工程の番号を引数に与え、

Utility::call( 1 )
Utility::call( 2 )
Utility::call( 3 )

とすれば、時系列で処理を行うことができます。

また、これらの手法を全てのユーティリティで適用することで、他のユーティリティへの切り替えも、

Utility_A::call( 1 )
Utility_B::call( 1 )
Utility_C::call( 1 )

とすることで簡単に行うことができます。

とはいうものの、一般的なプログラムとなんら変わったことをしているわけではありません。

この手法はBASICやDOSのパッチファイルを参考にしたのですが、このようなコード定義を嫌うプログラマが多く、その理由としてコードの行方が追いにくいといわれてますが、それは、コードの行数が多くなる場合に起こることで、行数が小さい場合は非常にスマートで合理的です。

フロントエンドのコード定義で具体的な処理を書くことができますが、コードの行数が大きくなるので、前述、コードの行方が追いにくくなります。

そのため、具体的な処理はサブルーチンとしてユーティリティ内に書き、フロントエンドのコード定義からはサブルーチンの呼び出しを定義することで解決しますが、このような手法も特別なことではなく、ごく一般的です。

バックエンド処理

バックエンド処理は利用者の目が届かないような裏方の処理を行います。

フロントエンドとバックエンドの明確な区分はありませんが、フロントエンド処理の具体的な処理を行うサブルーチンやデータ収集や問い合わせなどを行うサブルーチンを収録したものと捉えていただければいいと思います。

前述、フロントエンド処理をよびだすためのの関数call()も、区別をつけるならバックエンド処理の一部です。

イベントフック処理

イベントフック処理は必須ではありませんが、ユーティリティによって必要な場合にのみ定義します。

BBS.pmはユーティリティモジュールをロードしたとき、モジュール内に関数load()が含まれているとサーバ開始前に実行されます。

このload()は、BBS.pmのイベントにフックする処理を定義することができ、イベントの発動によってユーティリティの振る舞いを変えることができます。

例えばチャットを例とすると、あるノードがチャットルームに入室すると、チャットユーティリティのルーム管理を行う処理が行われますが、チャットルームに入室したノードがログアウトせず、接続を強制終了した場合、チャットユーティリティはノードが強制終了したことを知ることができません。

これはノード切断したことがチャットユーティリティに伝わらないためで、このような場合、切断イベント(onDisconnect)が発動したときにユーティリティに伝わるよう、イベントフックをします。

イベントフックにはユーティリティ内に次のような処理を定義する必要があります。

(1) フックするイベント処理(サブルーチン)を定義

イベントが発動したときに行う処理を記述します。

(2) 関数load()を定義

ここでは関数setsyshandler()で、フックするイベントとイベント処理ルーチンを設定します。 前述、チャットユーティリティでは切断イベントが発動したときにイベント処理(例えばルーチン名がApp::Chat::eventhook)を行うので、

sub load {
  $self = shift;
  $self->setsyshandler('onDisconnect',    sub { $self->App::Chat::eventhook(@_) });
}

のような形になります。

最後に

BIG-Modelアプリケーションのユーティリティは以上のような制約に従って作られていますが、特別なことは行っておらず、一般的なプログラミング技法と同じです。

コードの可読性やメンテナンスへの配慮は、工夫次第で解決できるかもしれません。

わからないことがありましたら、コメントへお願いします。

Task Runner