【PL/I学習|初心者向け】PL/Iプログラム分割術:外部手続きをマスターして保守性を高めよう

1. 導入:なぜプログラムを分割するのか?

メインフレームでのシステム開発は、時に膨大な量のプログラムコードを扱います。一つのプログラムが何千行、何万行にもなると、以下のような課題に直面します。

  • 保守性の低下: どこを修正すればよいか分かりにくく、変更が他の部分に予期せぬ影響を与えがちです。
  • 開発効率の悪化: 複数人で同時に作業するのが難しく、コンパイルにも時間がかかります。
  • 再利用性の欠如: 特定の処理を別のプログラムで使いたいと思っても、切り出すのが困難です。

このような課題を解決するために登場するのが「外部手続き (EXTERNAL PROCEDURE)」です。外部手続きを上手に活用することで、プログラムを小さな部品に分割し、それぞれの部品を独立して開発・テスト・保守できるようになります。これは、現代のプログラミング言語における「モジュール化」や「クラス分割」の概念と非常に似ています。

2. 基礎知識:外部手続きとは?

外部手続きは、個別にコンパイルできるプログラムの独立した単位です。メインのプログラムから呼び出されることを前提として設計されます。

  • 個別のコンパイルと結合:
    外部手続きは、それ自体が単独でコンパイルされ、オブジェクトモジュールという中間ファイルが生成されます。その後、メインプログラムのオブジェクトモジュールや他の外部手続きのオブジェクトモジュールと「リンカ(Binder)」と呼ばれるツールによって結合され、最終的に実行可能なロードモジュール(実行ファイル)が作成されます。
  • ENTRY属性による宣言:
    メインプログラムが外部手続きを呼び出す際には、呼び出す外部手続きの名前と、その手続きが受け取る引数の型、そして返す値の型を事前に宣言しておく必要があります。PL/Iではこれを「ENTRY属性」を使って行います。これにより、コンパイラは呼び出しが正しい形式で行われているかをチェックできます。
  • スコープの独立性:
    外部手続き内で宣言された変数は、その手続き内でのみ有効であり、他の外部手続きからは直接参照できません。これにより、プログラム部品間の独立性が保たれ、意図しない副作用を防ぐことができます。

3. 実装/解決策:プログラム分割のメリットと呼び出し方

プログラムを外部手続きとして分割する具体的なメリットは以下の通りです。

  • 開発の分担: 複数の開発者が異なる外部手続きを並行して開発できます。
  • 保守性の向上: 特定の機能に問題が発生した場合、その機能を持つ外部手続きだけを修正・再コンパイルすればよいため、影響範囲を限定できます。
  • 再利用性の確保: 汎用的な処理(例えば日付計算、データ変換など)を外部手続きとして作成しておけば、複数のプログラムから共通で利用できます。
  • コンパイル時間の短縮: 変更があった外部手続きのみを再コンパイルすればよいため、全体のビルド時間が短縮されます。

呼び出し方としては、メインプログラムから外部手続き名を指定し、必要な引数を渡す形になります。PL/Iでは、関数呼び出しのように記述します。

4. サンプルプログラム:外部手続きの基本

ここでは、PL/Iでメインプログラムが外部手続きを呼び出し、計算処理を行わせる簡単な例を示します。
メインプログラムと外部手続きは、それぞれ別のデータセット(ファイル)に保存されていることを想定しています。


// --- メインプログラムのソース (例: 'YOUR.PLI.SOURCE(MAINPROG)') ---
MAINPROG: PROC OPTIONS(MAIN);
    DCL (NUM1, NUM2) FIXED BIN(31); // 整数型変数の宣言
    DCL RESULT       FIXED BIN(31); // 外部手続きからの結果を受け取る変数

    // 外部手続き MY_ADDER を呼び出すことを宣言します。
    // MY_ADDER は2つのFIXED BIN(31)型の引数を受け取り、
    // FIXED BIN(31)型の値を返すことを示しています。
    DCL MY_ADDER ENTRY (FIXED BIN(31), FIXED BIN(31)) RETURNS (FIXED BIN(31));

    // 変数に値を設定
    NUM1 = 10;
    NUM2 = 25;

    PUT SKIP LIST('計算を開始します...');
    PUT SKIP LIST('入力値:', NUM1, NUM2);

    // 外部手続き MY_ADDER を呼び出し、結果を RESULT に格納
    RESULT = MY_ADDER(NUM1, NUM2);

    PUT SKIP LIST('外部手続きからの計算結果:', RESULT);

    PUT SKIP LIST('メインプログラムを終了します。');
END MAINPROG;


// --- 外部手続きのソース (例: 'YOUR.PLI.SOURCE(MYADDER)') ---
// MY_ADDERという名前の外部手続きを定義します。
// EXTERNALキーワードは、これが外部から呼び出される手続きであることを示します。
// RETURNS (FIXED BIN(31)) は、この手続きがFIXED BIN(31)型の値を返すことを示します。
MY_ADDER: PROC(X, Y) EXTERNAL RETURNS (FIXED BIN(31));
    DCL (X, Y) FIXED BIN(31); // 引数XとYの型を宣言します。
    DCL SUM_VAL  FIXED BIN(31); // 内部で使用する変数です。

    // 受け取った引数を加算します
    SUM_VAL = X + Y;

    PUT SKIP LIST('  (MY_ADDER): 呼び出されました。');
    PUT SKIP LIST('  (MY_ADDER): 引数X=', X, '引数Y=', Y);
    PUT SKIP LIST('  (MY_ADDER): 計算結果=', SUM_VAL);

    // 計算結果を呼び出し元(メインプログラム)に返します
    RETURN(SUM_VAL);
END MY_ADDER;

この2つのソースコードをそれぞれコンパイルし、その後リンカで結合することで、一つの実行可能モジュールが生成されます。

5. 応用・注意点:現場で役立つ情報

  • 引数の一致は絶対!:
    呼び出す側(メインプログラム)のENTRY宣言と、呼び出される側(外部手続き)のPROC宣言で、引数の数、型、順序が完全に一致している必要があります。これが少しでも異なると、コンパイルは通っても、実行時に予期せぬエラーや不正な結果を引き起こす原因となります。
  • リンカの名前解決ルール:
    メインフレームのリンカは、プログラム名やENTRY名を処理する際に、すべて大文字に変換するなどの特定のルールを持っている場合があります。PL/Iで小文字を使いがちな場合でも、実際のリンカでの名前解決時には大文字として扱われることを意識しておくと良いでしょう。
  • JCLでのビルド:
    実際の現場では、これらのコンパイルとリンクの作業はJCL(Job Control Language)を使って自動化されます。PL/Iコンパイラを呼び出すステップと、リンカ(IEWBLINKなど)を呼び出すステップが連なって実行されます。
  • 再利用可能な部品の作成:
    汎用性の高い計算処理やデータ操作などは、積極的に外部手続きとして切り出し、ライブラリ化(ロードモジュールライブラリに格納)することで、システム全体の開発効率と品質向上に貢献します。
  • 現代言語との比較:
    現代のオブジェクト指向言語における「パブリックメソッド」や「外部モジュール」と、概念的には非常に近いです。もし将来的にシステム移行の機会があった場合、これらの外部手続きは、新しい言語のクラスやモジュールの境界にマッピングされることが多いでしょう。

外部手続きは、メインフレーム開発において大規模なシステムを効率的に構築・保守するための非常に強力なツールです。ぜひこの基本をマスターして、日々の開発に役立ててください。

コメント

タイトルとURLをコピーしました