Sample : Client実行リストの構造化

実行リストは基本的には「上から下へ順番に実行」するような形で設定していきますが、 実際は「条件判断」等も必要となるため、いつも単純に順番どおりに実行すればよいとは限りません。
[公式Document] CAMC / 3. 実行リストで説明がある「SKIP」等の制御や、 さらに[公式Document] ClientJava / 実行マクロの機能を使って特定処理をSKIPしたりする方法もありますが、 どちらも条件判断や処理が複雑になったり複数行にわたる処理になったりする事も多く そういった場合にSKIP等の制御だけだと保守性が悪くなってしまうことがあります。 このような場合に標準ロジッククラスの「実行リストを構造化する」ロジッククラスを使用して 保守性、可読性を高めつつ複雑な処理を実装していく手法があります。

この手法について構造化ロジッククラスの使い方と、開発Toolの使い方を説明します。

1.処理ブロック+ExecuteAfter
2.IfElse
3.SwitchCase
4.IfElse/Switchの適用、非適用の法則

処理ブロック+ExecuteAfter↑top

処理ブロックを使用する方法は構造化ロジッククラスを使用しませんが、 実行リストを構造化する上で最も基本となる方法です。 処理ブロックの考え方を理解していなければ構造化ロジッククラスは使えませんし、 逆に処理ブロックを理解していれば後述するIfElseやSwitchCase等の構造化ロジッククラスを使用しなくても、 かなり保守性をあげる事ができます。

例1例えば、以下のような仕様があったとします。

構造化実装Sample01
分類 処理
初期処理 【データ1】の値が設定されている場合処理Aを実行。
処理Zを実行。

【データ1】に値が入っているときは「処理A」「処理Z」が実行され、 【データ1】に値が入っていないときは「処理Z」のみが実行されるという仕様です。

これを単純に実装すると次のようになります。
●図1. 構造化実装Sample01
images/01.jpg

例2ではこれが、次のような仕様だった場合。

構造化実装Sample02
分類 処理
初期処理 【データ1】の値が設定されている場合処理A、処理Bを実行。
処理Zを実行。

先ほどの例ではSKIP対象が「処理A」のひとつだったため何の問題もありませんでしたが、 今回の例ですとSKIP対象が「処理A」「処理B」のふたつです。
実行マクロの機能を使えば「SKIP2」のような記述もできますが、 これでは仕様変更があった場合等の保守性に欠けます。
そこで、次のように考えます。

「処理A」「処理B」を「データ1が設定されているときの処理」 というひとつにまとめて考えます。
そのひとつの処理をSKIPするかしないか、で実装してみます。
とりあえず、処理ブロックを利用して処理Aと処理Bをひとつにまとめてみます。 Client Application Management Consoleにある「処理ブロック」を使用します。

images/02.jpg

このように「複数の処理をひとつにまとめる」考え方が処理ブロックの考え方です。 そして、処理ブロックを実行する機能が「ExecuteAfter」という機能です。 処理ブロックを実行する標準ロジッククラスは3種類あります。 それぞれの名称とJavaDocから引用した説明を次に記述します。

  • AfterEvent実行(返り値 OK)(ExecuteAfterEvent)
    outに指定されたアイテムのAfterイベントを実行する。 in=0、out=複数。
  • AfterEvent実行(返り値 引き継ぎ)(ExecuteAfterTakeOver)
    outに指定されたアイテムのAfterイベントを実行する。 イベントから返ってきた返り値をそのままロジッククラスの終了値とする。 ただし、イベントがSKIP、SKIP_ALLで終わった場合は、 このロジッククラスはOKを返す。in=0、out=複数。
  • AfterEvent実行(Inの値が設定されたら終了)(ExecuteAfterUntilEmpty)
    In に設定された Text アイテムに値が設定されるまで Out に設定されたアイテムの After実行リストを順次実行する。

それぞれ特徴がありますが通常は 「AfterEvent実行(返り値 引き継ぎ)(ExecuteAfterTakeOver)」を使います。 最もSimpleな「AfterEvent実行(返り値 OK)(ExecuteAfterEvent)」を使った場合は、処理ブロック内で「CANCEL」等を返しても ExecuteAfterを実行した実行リスト内に処理ブロック内での返り値が返らないなど、 下位互換のためだけに存在しているあまり使用用途のないロジッククラスです。

今回のSampleの実装の続きをしますと、まずは先の手順で作成した処理ブロックを実行するための ExecuteAfterロジッククラスを「データ1が設定されているときの処理を実行する」という名称で定義します。

images/03.jpg

ここまでくれば、後は処理ひとつをSKIPするのと同じことですから、 実行リストの実装は以下のようにSKIPひとつで済みます。

images/04.jpg

今回の例ですと処理が単純ですから「階層」があっても大した問題はありませんが、 「可読性」という意味では表面上で処理ブロック内の処理が見えません。
この問題を回避するために、Client Application Management Consoleの実行リストのメニューに 「構造ツリー」という機能があります。
今回の実装を構造ツリーで見てみると次のように表示されます。

images/05.jpg

詳細設計書の記述も、このような実装を考慮して以下のようにインデントをつけて記述すると良いかもしれません。

構造化実装Sample02
分類 処理
初期処理 【データ1】の値が設定されている場合以下の処理を実行。
  処理Aを実行。
  処理Bを実行。
処理Zを実行。

IfElse↑top

次のようなSample仕様の実装を考えて見ます。

構造化実装Sample03
分類 処理
初期処理 【データ1】の値が設定されている場合。
  処理Aを実行。
  処理Bを実行。
そうでない場合。
  処理Yを実行。
  処理Zを実行。

単純にExecuteAfterのみを使用して以下のように実行リストを組み立てても良いかもしれません。

処理 ユーザエラー 実行マクロ
データ1存在チェック SKIP
データ1が設定されているときの処理を実行する
データ1非存在チェック SKIP
データ1が設定されていないときの処理を実行する

これでも仕様どおり正しく動作しますので問題はありません。 しかし、例えば条件判断がもっと複雑になった場合はどうするか? などを考えた場合に、 通常のProgrammingでいう「IfElse」の様な構造を実装する構造化ロジッククラスが存在します。

  • IfElse構造化ロジッククラス(StructureIfElse)
    Inに複数アイテム、OutにInより1つ多いアイテムを設定する。 InのAfterイベントを順に実行し OK、SKIP、SKIP_ALLの何れかが返ってきたとき、 以降のInのイベントは行なわなず同一行のOutItemを実行する。 全てがOKの場合は最後のOutを実行する。

この説明を図で示すと以下のようになります。

IfElse構造ロジッククラスの説明
In Out
条件判断1処理ブロック 条件判断1が成立した場合の処理ブロック
条件判断2処理ブロック 条件判断2が成立した場合の処理ブロック
全ての条件判断が成立しなかった場合の処理ブロック

IfElseロジッククラスは最初にIn(0)に設定された条件判断処理ブロック を実行し、 条件判断処理ブロックの返り値が「CANCEL」でなかった場合 、 つまり条件が成立していた場合はOut(0)に設定された処理ブロックが実行されます。 逆に、In(0)に設定された条件判断処理ブロックの返り値が 「CANCEL」だった場合はOut(0)に設定された処理ブロックは実行されずに In(1)に設定された条件判断処理ブロックの返り値の評価を行います。
このように、Inに設定された条件判断処理ブロックが「CANCEL以外を返すまで」 条件判断処理ブロックを次々に実行していき、 「CANCEL以外を返した、つまり条件判断が成立した条件判断処理ブロック」 があった場合はその条件判断処理ブロックに対応する、 Out側の同じIndexに設定された処理ブロックを実行します。
Inに設定された条件判断処理ブロック全てが「CANCEL」を返した場合、つまり どの条件判断も成立しなかった場合はOutの一番最後に設定された 処理ブロックが実行されます。
上の説明がどのような動作をするかは以下のようなProgramでも表現できます。

では、 構造化実装Sample03のSample仕様の実装方法を考えて見ます。

まずは処理ブロックは全部でみっつ必要なことがわかります。

  • 条件判断
    【データ1】に値が設定されているか
  • 処理1
    処理A、処理B
  • 処理2
    処理Y、処理Z

これをIfElseを使って実装すると「条件判断」が「In」に 「処理1」「処理2」が「Out」に設定される必要があります。

それぞれ必要な「処理ブロック」「IfElseロジッククラス」を手動で設定しても良いですが、 構造化用の処理ブロックやロジッククラスは「構造ツリー」を使って設定することも可能ですのでその方法で実装して見ます。


IfElse構造作成の手順
  1. 今回のSampleは「実行ボタン」に処理を設定しているため、まずはアイテムの実行ボタンの実行リストを開きます。
  2. 実行リストのメニューにある「構造ツリー」機能を起動します。
  3. 表示された構造ツリーの「New」メニューから「IfElse構造の作成」を選択します。
    images/11.jpg
  4. 作成するIfElseロジッククラスの名称を入力するDialogが表示されるので適当な名称を入力します。
    images/12.jpg
  5. IfElseロジッククラスが作成されたら、まずは条件判断処理ブロックを作成します。 作成されたIfElseロジッククラスをツリー上で選択してメニュー「New」を開き、 「条件判断ブロックの作成」を選択します。
    images/13.jpg
  6. 作成する条件判断処理ブロックの名称を入力するDialogが表示されるので適当な名称を入力します。
    images/14.jpg
  7. 条件判断処理ブロックはひとつでよいので、 次は条件に対応する処理ブロックの作成を行います。 IfElseロジッククラスをツリー上で選択してメニュー「New」を開き、 「処理ブロックの作成」を選択します。
    images/15.jpg
  8. 作成する処理ブロックの名称を入力するDialogが表示されるので適当な名称を入力します。
    images/16.jpg
    今回はさらにもうひとつ条件が成立しなかった場合、 所謂「Else」に対応する処理ブロックを作成する必要がありますので それも同じ手順で作成します。
  9. これらの作業が終わるとツリーでは次の図の様な構造が表示されているはずです。
    images/17.jpg

ここまでの手順で「構造」の部分は作成できました。 後は各処理ブロックに処理を設定していくだけです。


構造の中身の実装をする手順
  1. まずは条件判断処理ブロックの中身を実装します。
    ツリーで条件判断処理ブロックに相当するNodeを選択し、 「対応する構造へジャンプ」ボタンを押下します。 するとClient Application Management Console上でこの処理ブロックが選択状態になります。
  2. 条件判断処理ブロックは条件が成立しなかった場合は、 最終的に「CANCEL」を返す必要がある事に注意が必要です。
    images/18.jpg
  3. 通常の処理を行うだけの処理ブロックの中身の実装方法は省略しますが、 全ての処理ブロックの設定が終わった時にはツリーの中身は次のようになっているはずです。
    images/21.jpg
■Point
「条件判断」を行い、その判断結果により処理を振り分けるためのIfElseロジッククラスですが、 例えば「条件判断」自体が複雑な場合に、その条件判断を行うために階層を深くしてIfElseを使用するのは 本来の処理の振り分けとは関係のないIfElseが発生する ことになり危険です。条件判断自体が複雑な場合は、その判断処理自体を単独の独自ロジッククラスとして作成するほうが効率は良いです。 特に、ほとんどの条件判断はアイテムの値の比較と値の操作だけで済みますから、 JUnit等のToolを使用して作成した独自ロジッククラスの単体試験を簡単に行うことができるため、 高い精度で「実装、試験、保守」の繰り返し運用が可能になります。

SwitchCase↑top

次のようなSample仕様の実装を考えて見ます。

構造化実装Sample04
分類 処理
初期処理 ラジオボタン【操作モード】の値が”A”(登録)の場合。
  処理Aを実行。
  処理Bを実行。
ラジオボタン【操作モード】の値が”B”(更新)の場合。
  処理Cを実行。
  処理Dを実行。
ラジオボタン【操作モード】の値が”C”(削除)の場合。
  処理Yを実行。
  処理Zを実行。

先に説明があった「IfElseロジッククラス」を使用して次のように実装する事もできます。

構造化実装Sample04をIfElseで実装
In Out
【操作モード】の値が”A"か判断する条件判断処理ブロック 処理A、処理Bを実行する処理ブロック
【操作モード】の値が”B"か判断する条件判断処理ブロック 処理C、処理Dを実行する処理ブロック
【操作モード】の値が”C"か判断する条件判断処理ブロック 処理Y、処理Zを実行する処理ブロック
何もしない(何も設定されていない)処理ブロック

今回は明らかに【操作モード】が”A"”B"”C"の3種類しかなく、 それ以外の想定が不要であるとした場合Programでいう「Switch」と同じ処理を実行するロジッククラスが適用できます。

  • SwitchCase構造化ロジッククラス(StructureSwitchCase)
    In に設定された Text アイテムの数値に対応する Out アイテムの実行リストを実行する。 (値0がOutの一番目、値1がOutの二番目…)

この説明を図で示すと以下のようになります。

Switch構造ロジッククラスの説明
In Out
処理番号アイテム 処理番号アイテムの値が0だったときの処理
処理番号アイテムの値が1だったときの処理
処理番号アイテムの値が2だったときの処理

まず、条件に応じて「処理番号」のような処理の振り分けを行うためのアイテムに、 0からの値を設定してからこのロジッククラスを実行すると その値に応じたOut側の処理ブロックが実行されます。
まず処理番号として使用するアイテムに値を設定する必要があるため面倒に思えますが、 「Server Application Management Consoleのディシジョンテーブル-ディシジョンタイプ」の構造に似ているため、人によっては使いやすい場合もあるかもしれません。

また、特定アイテムに値を設定する時に使用するロジッククラスとして、 先のExecuteAfterのときに紹介した以下のロジッククラスが使用できます。

  • AfterEvent実行(Inの値が設定されたら終了)(ExecuteAfterUntilEmpty)
    In に設定された Text アイテムに値が設定されるまで Out に設定されたアイテムの After 実行リストを順次実行する。

Outに設定された処理ブロックを順次実行する仕様は基本的にはExecuteAfterと同じですが、 違う点として「Inに設定したアイテムに値が設定されたタイミングでOutの順次実行を中断する」 という仕様です。「Switchロジッククラス」と対にして使用されることが多いため、「SetCase」という呼ばれ方もします。
具体的な使い方については実際に使ってみればすぐに理解できると思いますので、 構造化実装Sample04の仕様を参考にしつつ説明します。

まずは仕様書に少し手を加えてSwitchの実装用に見やすくしてみます。 処理番号を記述しただけですが、手を加えた箇所は太字で表現しています。

構造化実装Sample04
分類 処理
初期処理 ラジオボタン【操作モード】の値が”A”(登録)の場合(処理番号0)
  処理Aを実行。
  処理Bを実行。
ラジオボタン【操作モード】の値が”B”(更新)の場合(処理番号1)
  処理Cを実行。
  処理Dを実行。
ラジオボタン【操作モード】の値が”C”(削除)の場合(処理番号2)
  処理Yを実行。
  処理Zを実行。

  1. まずは処理番号用のアイテムを作成します。
    このアイテムは画面の仕様とは関係のないアイテムですから、他のSwitch構造で使いまわしてもかまいません。
  2. 今回ですと「実行ボタン」に処理を実装していきますので、実行リストを開いて「構造ツリー」メニューを開きます。
  3. まずは処理番号を設定するための構造を作成します。
    構造ツリーの「New」メニューから「SetCase構造の作成」を選択します。 images/31.jpg
  4. 作成する「SetCaseロジッククラス」の名称を設定します。
    images/32.jpg
  5. 次に、処理を振り分けて実行する構造を作成します。
    構造ツリーの「New」メニューから「SwitchCase構造の作成」を選択します。 images/33.jpg
  6. 作成する「SwitchCaseロジッククラス」の名称を設定します。
    images/34.jpg
  7. 次は「SetCaseロジッククラス」の中に処理番号を設定するための処理ブロックを作成します。
  8. ツリー上で、作成した「SetCaseロジッククラス」を選択して「処理ブロックの追加」を選択し、作成する処理ブロックに名称をつけます。
    今回は「0」「1」「2」を設定するみっつの処理ブロックが必要ですからこの手順を繰り返してみっつ準備します。 images/35.jpg
  9. 次は「SwitchCaseロジッククラス」の中に処理番号に対応した処理を行う処理ブロックを作成します。
  10. ツリー上で、作成した「SetCaseロジッククラス」を選択して「処理ブロックの追加」を選択し、作成する処理ブロックに名称をつけます。
    今回は処理番号の値が「0」「1」「2」に対応するみっつの処理ブロックが必要ですからこの手順を繰り返してみっつ準備します。
  11. ツリー上では下の図のようになっているはずです。
    images/37.jpg
  12. 忘れないように、「SetCase」「SwitchCase」のロジッククラスのInに「処理番号」として使用するアイテムを設定しておきます。
    images/38.jpg images/39.jpg
  13. これで構造の準備はできました。後はそれぞれの処理ブロックの中に処理を配置していくだけです。
  14. まずはSetCaseの中の処理番号設定のための処理を配置します。
    ここでは単純に各処理ブロックに「値判断->処理番号設定orSKIP」というロジックを組み込みました。 説明は不要だと思いますが、値の判断には「CheckEqualParameter」を、 値の設定には「SetValue」ロジッククラスを使用しています。 images/40.jpg
  15. 次にSwitchCaseの中の処理番号に対応した処理を配置します。
    この中は簡単なため細かい説明は記述しませんが、全て完了すると図のような構造になります。 images/41.jpg

IfElse/Switchの適用、非適用の法則↑top

ここまでの説明で「ExecuteAfter」「IfElse」「Switch」の3種類の構造について説明を行いました。 が、いくつかの場面でどの構造を適用しても実装できる 、または違う表現方法だといくつか実装方法の種類がある という状況がありました。 これに関して「どの方法が良いのか?推奨されているのか?」という疑問に対しての公式な回答はありません。 また、構造ロジッククラスや処理ブロックの「命名規則」についても同様です。 しかしなんらかの規則を設けることを推奨します。 あまり深くなりすぎると解析をする時の手間がかかるかもしれませんし、 かといって何も適用しないとなると実行リスト内の構成が大変なことになってしまいます。

例えば、次のような「階層の深さ」や「IfElse/Switch」の適用規則を設けると良いかもしれません。

  • 3階層以上にはしない
  • 2値の判断には必ずIfElseを適用する
  • 「画面操作モード」等、値の種類が決まっている(IfElseでいうElseの想定が不要)ものは必ずSwitchを適用する
  • 構造ロジッククラスは「Execute_」「IfElse_」「SetCase_」「Switch_」をPrefixとして使用する。
  • 処理ブロックは「Block_」「If_」「Case_」「Else_」をPrefixとして使用する。

など、これはあくまでも一例にすぎませんが、なんらかの規則があった方が良いのは間違いありません。

■注意
「命名規則」については、厳しくしすぎると「命名するためのコストがかかりすぎる」場合があります。 かといってゆるくしすぎると「対象のロジッククラスや処理ブロックを探せない」等の問題を引き起こす場合もあります。 対象のロジッククラスや処理ブロックを探す作業は「構造ツリー」「一覧内でCtrl+F」等の機能で軽減されますが、 基本的には何らかの規則はあった方が便利におもいます。

Valid XHTML 1.1