RDi V9.6 パート9、RDiを使用してコードをプロシージャーにリファクタリングする
読者の皆さんのショップにも、何らかのリファクタリング(コードの内部構造の改良)を切実に必要としているRPGコードがあるのではないでしょうか。そうした状況に置かれていないRPGコードは、私はまだ目にしたことがありません。「リファクタリング」とはどのようなことなのか、あるいは、どうして必要になるのかについてピンとこないという方には、RPGのリファクタリングについてTed Holtが記した、優れた一連のGuru記事をお読みになることを強くお勧めします。記事末尾の「関連記事」の欄に、それらの記事のリンクを示します。
それらの記事シリーズを全般的に読み直してみようと思ったのは、RPG & DB2 SummitイベントでTedが行ったセッション「 Refactoring RPG: What, Why & How (RPGのリファクタリング: 何、なぜ、どのように)」に参加したことがきっかけでした。Tedのセッションと記事シリーズはどちらも、コードをより理解しやすくし、保守や拡張を行いやすくするために誰もが行うことのできる具体的なステップに重点が置かれています。
それらの記事シリーズの最初の記事では、RDiに備わるツールをどのようにリファクタリング プロセスに利用できるかについて説明がなされています。RDiの「アウトライン」には、手作業でリファクタリングを行う際に有用な情報が表示され、また、「リファクタリング」 > 「Rename . . .」機能を使用して、実際にいくつかのコード変更の実装も行っています。
Tedがリファクタリングに関して記した2番目の記事(「 Refactoring into Routines (ルーチンへのリファクタリング)」)では、コード ブロックを最初にサブルーチンへ変換し、次いでサブプロシージャーへ変換しています。その記事の執筆当時は、RDiには、それらのどちらのステップについても頼りにできる機能がありませんでした。しかし、V9.6.0.7で、RDiに「リファクタリング」 > 「Extract Procedure . . .」機能が備わりました。今回の記事では、Tedオリジナルのリファクタリングのサンプル コードを使用して、その新機能の使い方について見て行こうと思います。
図1は、Tedの2番目の記事の冒頭にあるコードです(すでにオリジナルのコードから大幅に改良されています)。固定フォーマットのRPGコードであることをあらかじめご了承ください。この点に関しては行えることがありますが、それについては後述します。
Tedが記事の中でリファクタリングしたのは、割引パーセンテージを計算するSELECT構造でした。ここでも同じことを行います。Tedの記事では、行うべき大きな判断は、データ定義およびデータ フローに関するものであり、目的は、プロシージャーがグローバル データをまったく使用しないようにすることであると説明されています。計算で使用される値に対してパラメーターがプロシージャーに渡され、計算結果が呼び出し元に返されます。プロシージャー ロジックで必要となる可能性がある他のどの変数(または実際にはファイル)も、ローカル変数(またはファイル)として定義できます。この例では、ローカル変数として必要とされるのは、割引パーセンテージのみです。
そうした判断を行ったら、サブプロシージャーの作成という手間のかかる作業はRDiに任せることとしましょう。図2に示すのが、最初の手順です。すなわち、サブプロシージャーに変換されることになるロジック ブロックを選択して、「リファクタリング」 > 「Extract Procedure . . .」を選択します。
次いで、図3のような「RPG Procedure Wizard」が表示されます。
このスクリーン ショットには、このサンプル コード ブロックに対するウィザードの初期設定値が示されています。プロシージャー名が提案されていることに注目してください。判定された値を dispct 変数に入れる( get into)ことがこのプロシージャーの目的であることを(正しく)解明した上で名前を提案しているのだとすれば、なかなかの芸当だと言えるでしょう。
戻り値がどのようなものになるのかを判定するために、どのようにロジックが動作するのかについては、私も正確には知りません。けれども、これまで見てきた限りでは、ほとんどの場合、正しく選択されているようです。中には、戻り値が提案されなかった(プロシージャー名も提案されなかった)ケースも1つはありました。これは既存のコードに明らかな戻り値がなかったためです。そのケースでは、成功/失敗の標識として戻り値を追加しました。もっとも、戻り値のことをまったく気にしないケースも多いかもしれません。これまでのところ、不適切な戻り値が選択されたケースは1つあったのみでした。ただし、それが異常なコード ブロックであったことは認めておく必要がありそうです。
プロセスが戻り値(ある場合)として何を選択するかについて注目することは重要だということが分かりました。正しい選択が行われていれば、他にも多くの有用な処理を行ってもらうことができます。たとえば、実際に生成されたコードでいくつかの変更を行って、異なる名前で変数のローカル バージョンを作成し(デフォルト)、次いで、プロシージャー内にあるすべてについて、古い名前を新しい名前に置き換える処理です(「リファクタリング」 > 「Rename . . .」機能に相当)。
後ほど、そうした戻り値サポート機能が、SELECTブロックでどのように利用できるかについて説明するつもりです。ただしそれはこの後で表示されるウィザード画面で行う操作であり、先にここで行っておく必要があることがいくつかあります。まず、プロシージャーに3つの入力パラメーターを追加する必要があります。これは、最初のウィザードの画面で行います。
図4には、「 Add... 」ボタン(図3の下部でハイライトされています)を押してポップアップしたダイアログと、パラメーターの1つについて入力した内容が示されています。パラメーター キーワード(私のお気に入りのCONSTを含めて)が、チェック ボックスをチェックすることで指定できることに注目してください。別のパラメーターを定義するには、「 OK 」(またはEnter )を押して最初のウィザード ページへ戻ってから、定義するパラメーターごとに、「 Add...」を押し直す必要があります。
プロシージャー ウィザードの最初のページを終了する前に、Tedが使用した名前を使用するようにプロシージャー名を変更しようと思います。これで、「Purpose(目的)」をより分かりやすく表したプロシージャー名になります。図5は、最初のウィザード ページの最終バージョンを示しています。
これで「 Next 」ボタンを押すことができるようになりました。それでは、生成されるコードに対して戻り値の選択がどのように影響するかについて見てみましょう。この時点で「 Next」ではなく「 Finish 」ボタンを押してしまうと、戻り値の特性を修正する機会が失われてしまうことに注意してください。図6に、ウィザードによって最初に表示された、戻り値を定義するダイアログを示します。
dispctにあったことから、戻り値にp_dispctという名前が提案されているようです。経験から言うと、接頭辞のp_は、慣例的に、「~へのポインター」を表すのに使用されることが多いように思います。そのため、その名前を使用した場合、今後、このコードを見るたびに混乱の元になりかねません。特に、サブプロシージャーでローカル値のポインターを返すのは好ましいことではないからです。代わりに、生成された名前を、Tedが使用した名前、DiscountFactorに変更しました。また、ウィザードから提案された、 dispct のように定義するというオプションは使用せずに、タイプと長さを手入力して定義します。これは、グローバル変数のように定義することを避けたいからです。図7に、変更を行った後のパネルを示します。
「Finish」を押すと、このプロシージャーのコードが生成されます(図8)。以前の dispct はすべて、ローカル変数として定義された、私が戻り値に選んだ名前、DiscountFactorに名前変更されていることに注目してください。ウィザードは、コメント ブロック、始めおよび終わりのP仕様書、PI、および3つのパラメーターを生成しています。
また、ウィザードは、プロシージャー コード本体に加えて、オリジナルのSELECTブロックが置かれていたメインライン コードに、そのプロシージャーの呼び出しも生成しました。図9に、その呼び出しを示します。ここで渡されるパラメーター名を、プログラムでそれらの値に使用される変数名に一致するように手作業で変更する必要があります(CusCls、DisCod、Qty)。
興味深いことに、生成された他のすべてのコード(およびこのソース メンバーの他のすべてのコード)が固定フォーマットであるのに、プロシージャーの呼び出しがフリー フォーマットで生成されました。ここで使用されたすべてのコードがどうして(私の好みでもある)フリー フォーマットでなかったのか、疑問に思われていた方も多いことと思います。これは、使用したオリジナルの例が固定フォーマットだったことから、それに従ったというだけのことです。フリー フォーマット コードを生成するウィザードのオプションを指定することもできましたが(図10を参照)、オリジナルのロジックはフリー フォーマットに変換されませんし、フォーマットが混在することになるのが嫌でした。実際に行うとすれば、このプロセスを開始する前にオリジナル コードをフリー フォーマットに変換してから、フリー フォーム コードを生成させるオプションを使用するのが最善のやり方だと思います。
また、ウィザードが、プロトタイプを生成したり、外部プロシージャーとして使用できるように適切なキーワードを生成したりすることができるのかどうかについても疑問に思われるかもしれません。それらの疑問に対する答えは、両方とも「できる」です。これは、最初のウィザード ダイアログ ページの「Options」タブで指定できます(図10を参照)。フリー フォームの生成コードを指定するのと同じタブです。
RDiのリファクタリングに関する最新の機能は、いかがでしたでしょうか。ご覧の通り、RDiは細かな手作業でのコーディングを大幅に減らしてくれます。作業はほぼ自動化されていることもあり、最新のRDiのリファクタリング機能を利用することによって、多くのRPG開発者がリファクタリングをもっと行うようになることが望まれます。プロシージャーを自分の気に入るようにするためには、まだまだ手入力での変更作業が必要になるとしても、RDiが行えることの多さには感心させられます。これは初めの一歩であり、今後、さらなる機能強化がもたらされることと思います。命名規則やコードのフォーマットなどを処理できるように、さらに多くのカスタマイズ(おそらく設定オプションを通じて)を行えるようになればと思っています。
プロシージャーが何を返すかについて、RDiは非常に正確に解明できることは分かりましたが、RDiの選択に同意できないケースもきっとあると思います。RDiの選択は、変更させることはできないようです。戻り値を名前変更しようとすると(プロシージャー内の別の変数の名前への変更の場合でも)、RDiは、元の候補となる変数をすべて、私が変更しようとした名前へ変更して、その値を返そうとします(元に戻す機能のおかげで、プロセスを初めからやり直すことができるのはありがたいことです)。今後、RDiの判断に同意できないときに、RDiにその旨を伝えるオプションが追加されると良いと思います。今のところは、私にとって間違った戻り値をRDiが選択したときには、戻り値は必要ないとRDiに伝えてから、戻り値を処理するためのコードを手作業で追加するのみです。