NEWS
IBM i の”新”必須言語 〜FFRPG入門〜 IBM i の”新”必須言語 〜FFRPG入門〜
2019.01.28

【FFRPG】第九回 サービス・プログラムの作成 (1)

【FFRPG】第九回 サービス・プログラムの作成 (1)
第一回
とりあえず作ってみよう!
第四回
データベース – SQL(照会プログラム)
第七回
データベース – ネイティブ・アクセス(更新、削除プログラム)
第十一回
サービス・プログラムの作成 (3)
第二回
エディタとかエミュレータとか・・・
第五回
データベース – SQL(更新・削除・追加プログラム)
第八回
印刷したり画面に表示したり
第十二回
IBM i でプログラムを作るということ
第三回
データベース – SQL(照会プログラム)
第六回
データベース – ネイティブ・アクセス(照会プログラム)
第十回
サービス・プログラムの作成 (2)

前回の復習

第八回 印刷したり画面に表示したり」では、印刷装置ファイルと表示装置ファイルという2つのファイルの入出力を通して、同じ演算命令を使って異なる出力になることを確認しましたがいかがでしたか?IBM i は 30 年を超える長きに渡り使われてきたので、クライアント・サーバーやインターネットが普及する前のインターフェースとして、5250 画面やプリンターの印刷が標準でした。過去の資産の継承により、現在でもこのプログラムは利用されています。その仕組は独特ですが、ファイルというオブジェクトを通して主要な処理が行われていることを理解してください。RPGⅢで構築されたシステムは、入力処理も出力処理もこのファイルへのアクセスが中心です。このポイントをしっかり抑えておきましょう。 それでは、今回からサービス・プログラムについて解説していきたいと思います。RPGⅢでは実現できない業務ロジックの部品化の方法を通して、保守しやすいシステムを構築する方法を解説します。さっそくはじめましょう!  

関数について

まずは関数についてまとめておきたいと思います。これまでの連載で作成したプログラムで、皆さんはすでに関数を使用しています。例えばレコードが検索できたかどうかの判断に使用した %EOF や %FOUND、直前の命令でエラーが起こったかどうかを判断する %ERROR などです。これらを特に組み込み関数(Built In Function : BIF)といいます。V7.3 ではこの組み込み関数が  71 個提供されています。 この組み込み関数を使用する際に知っておくべきことは、関数名とその関数が必要とする引数、および戻り値です。例えば、%FOUND 関数の引数と戻り値は以下の通りです。
  • 引数
    • ファイル名(省略可能)
  • 戻り値
    • 見つかった場合は ‘1’ 、そうでなければ ‘0’
この情報さえあれば、その関数内でどのような処理が行われているのかを考える必要はありません。レコードをどのように検索しているのかとか、どの言語で書かれているのかとか一切知る必要はないのです。必要なのは引数と戻り値のみです。 ちなみに、%FOUND 関数は以下の命令の結果を取得する目的で使用します。
  1. CHAIN(ファイルからのランダム検索)
  2. DELETE(レコードの削除)
  3. SETGT(より大きいレコードにポインターを設定)
  4. SETLL(下限値にポインターを設定)
  5. CHECK(文字の検査)
  6. CHECKR(逆向きの検査)
  7. SCAN(ストリングの走査)
  8. LOOKUP(テーブルまたは配列要素の検索)
ファイルの I/O 命令の結果を受け取る以外の目的でも使用できることに注意しましょう。引数にファイル名を指定した場合は、そのファイルに対して最後に使用した関連命令の結果を戻しますが、ファイル名を省略すると、最後に実行した上記 8 命令のうちのどれかの結果を戻します。 組み込み関数毎に使用目的や引数、戻り値はそれぞれ異なりますので、マニュアルを参考に適切な関数を選択するようにしてください。 組み込み関数は、どのような形態のプログラムであっても汎用的に使用可能な機能のみを提供するものであり、全てのプログラマーが望む機能が網羅されているわけではありません。例えば引数に金額をセットすれば税込み価格が戻ってくる関数は組み込み関数として提供されてはいないのです。消費税計算は様々なプログラムで必要とされるので、組み込み関数と同様に定義することができれば、税率の変更やロジックの変更などが発生しても関数の内部ロジックを修正するだけで対応することができて便利ですね。 このようなユーザー独自の関数を定義して、複数のプログラムから組み込み関数のように参照できる機能を提供してくれるのがサービス・プログラムです。  

サービス・プログラム

もう少し具体的に話をしていきましょう。第三回で作成したプログラム icafe003 を思い出してください。このプログラム内で金額に対する税込価格を計算するサブ・プロシージャー getIncludeTax を定義しましたね。これにより main モジュールでは以下のように記述するだけで、税込価格を取得して画面に表示することができました。 dsply getIncludeTax(2200); このサブ・プロシージャーはこのプログラム内ではどこからも呼び出すことができます。しかし、他のプログラムから呼び出すことはできません。 消費税計算というどのプログラムでも使う共通のロジックでも、この仕組ではそれぞれのプログラムで定義しなければならず、将来税率が変わる場合の対応も煩雑になってしまいます。そこで、複数のプログラムで共通で利用できるようにサブ・プロシージャーを定義する方法が必要になってきます。これを提供するのがサービス・プログラムです。 サービス・プログラムはオブジェクトタイプが *SRVPGM であり、複数のプログラムで使用するサブ・プロシージャーを含みます。定義箇所が一箇所になるので、たとえば消費税率が変わった場合でも、サービス・プログラム内の税率 SALES_TAX を更新するだけで、それを参照するプログラムは何も変更することなく新しい税率での結果を取得することが可能になります。もちろん、各プログラムは税率計算のロジックがどのように行われているかを知る必要はないのです。  

サービス・プログラムの作成

サービス・プログラムのオブジェクト・タイプは *SRVPGM であり、プロシージャーを含んだ *MODULE オブジェクトが一つ以上必要です。オブジェクトの作成には CRTSRVPGM コマンドを使用します。 一つのモジュールの中には複数のサブ・プロシージャーを定義することができます。このため、ユーザー関数を一つのモジュールにまとめ、サービス・プログラム内にモジュールが一つという構成にすることもできるし、ユーザー関数の機能単位でモジュールを分け、それらを一つのサービス・プログラムにまとめることも可能です。 サービス・プログラムは、あくまでも他のプログラムから参照するサブ・プロシージャーをまとめているオブジェクトであり、通常のプログラムのように CALL で実行することはできません。そのため、サービス・プログラムを構成するモジュールにはメイン・プロシージャーは必要ない点に注意してください。 また、*PGM オブジェクト作成時に利用した CRTBNDRPG のようなコマンドはないため、モジュール作成のコマンドおよびサービス・プログラム作成コマンドをそれぞれ実行する必要もあります。 それでは一番シンプルなサービス・プログラムを作成してみましょう。getIncludeTax 関数を含んだモジュールを作成し、サービス・プログラムを作成します。以下がソースです。 まず、制御使用ステートメントに nomain キーワードを指定しています。これを指定することにより、このモジュール内にはメイン・プロシージャーがないことをコンパイラーに指示します。先ほど説明したように、サービス・プログラムは CALL で呼び出すことはできず、したがってメイン・プロシージャーを必要としません。サービス・プログラムに含まれるモジュールは常に nomain を指定して作成するようにしましょう。 続いて dftname キーワードを使用してモジュールの名前を指定しています。CRTRPGMOD のモジュール名の省略値は *CTLSPEC であり、dftname キーワードに指定した名称が使用されます。 サブ・プロシージャー getIncludeTax は、icafe003 内の指定と同じロジックですが、プロシージャーの定義ステートメントに EXPORT キーワードを追加しています。モジュール内に定義されたサブ・プロシージャーのうち、EXPORT キーワードを指定したものだけが、外部プログラムから参照することができます。忘れないように定義しましょう。 それではサービス・プログラムを作成してみましょう。Orion にログインし、先ほどのコードを icafesrv.rpgle というファイル名で登録してください。 ソースを登録したら、コンパイルを実行しましょう。IBM i Access for Web mobile のエミュレータにサインオンし、メニューのオプションでサービス・プログラムを作成します。先ほど説明したように、CRTBNDRPG のようなソース・コードから直接サービス・プログラムを作成してくれるコマンドはないので、まずはモジュールから作成します。icafe 実習メニューのオプション 12 を実行してください。 ソース・ストリーム・ファイルに先ほど登録したファイル名 icafe/icafesrv.rpgle を指定して実行します。 「モジュール icafeSRV がライブラリー IAFExxxxx に入れられました。」とメニュー画面の最下部に表示されたことを確認してください。モジュールが作成されていなければ、ソース・コードを再確認して間違いを修正し、再度オプション 12 を実行してください。 続いて作成されたモジュールを含むサービス・プログラムを作成します。メニューのオプション 13 を実行します。 作成するサービスサービス名には「icafeSRV」を指定してください。このサービス・プログラムに含めるモジュールは先ほど作成した icafeSRV です。コマンド実行後、「サービス・プログラム icafeSRV がライブラリー icafexxxxx に作成された。」と表示されたことを確認してください。  

サービス・プログラムを使用するプログラムの作成

それでは、このサービス・プログラムに定義された関数を使用するプログラム icafe023 を作成しましょう。サービス・プログラムを使用するプログラムを作成する場合に注意する点が2つあります。
  • 関数が含まれるサービス・プログラムの指定
  • プロト・タイプの定義
まず、作成しようとしているプログラムが使用するサービス・プログラムは、プログラム・オブジェクト作成時(CRTPGMコマンド実行時)に指定します。モジュール・オブジェクトのコンパイル時ではありません。このため、コンパイル時点では、ソース内部に指定されている関数がどこに定義されているのか、パラメータの数や属性などがあっているかを判断する術がないのです。そこでこれらの情報をコンパイラーに与えるためにプロト・タイプ定義が必要になります。 プロト・タイプ定義には以下が含まれます。
  1. 呼び出すプロシージャーの名前
  2. 渡すパラメータの数および属性
  3. 戻り値の属性
プログラム内部で使用するユーザー関数毎にプロト・タイプ定義を指定することにより、7030 エラー(未定義エラー)を回避することができます。 プロト・タイプ定義は、プロシージャー・インターフェース定義ととても良く似ています。dcl-pi / end-pi が dcl-pr / end-pr に変わっただけです。ただし、プロト・タイプ定義は、あくまでもコンパイラーに対して名前やパラメータの数や属性などを知らせるためであり、それ以上の機能はありません。この例では excluding_tax という名前が指定されていますが、コンパイラーが使用するのは属性値(packed)およびキーワード(value)通りにその関数がプログラム内部で使われているかをチェックするだけで、excluding_tax という変数が定義されることはないのです(プロシージャー・インターフェース内に指定されたものは変数として定義されます)。この曖昧さを回避するために、以下のように記述することも可能です。 では、icafe023 のソース全体をみていきましょう。 icafe023 内ではメイン・プロシージャーのみが定義されており、その中で getIncludeTax が使用されています。この関数は先ほど作成したサービス・プログラム内に定義されているものを使用するので、このソースの中ではプロト・タイプ定義のみを記述します。それではソースをコンパイルしてモジュールを作成しましょう。メニューのオプション 12 でコンパイルしてください。 icafe023 モジュールが作成されたので、次にプログラム icafe023 を作成しましょう。このプログラムには、icafe023 モジュールを含め、サービス・プログラム icafesrv を参照するように指定する必要があります。メニューのオプション 14 を選択してださい。 作成するプログラム名とモジュール名ともに icafe023 を指定します。この画面ではサービス・プログラムを指定する入力域がありませんが、これはオプションになりますので、画面下の「追加のパラメーター」ボタンをクリックしてください。するとサービス・プログラムを指定する画面が表示されるので以下のように指定してコマンドを実行してください。 プログラムが作成されたら、メニューのオプション 2 で実行してみましょう。結果は dsply で表示するので、オプション 5 を選択し、「詳細メッセージの表示」をクリックして確認してください。DSPLY  2376 と表示されましたか?  

サービス・プログラムの変更

今年は消費税率の変更が予定されていますね。今回のサービス・プログラムの税率は 8% ですがこれを 10% に変更する手順は以下の通りです。
  1. ソースの 8% の部分を 10% に修正
  2. モジュールを再作成
  3. サービス・プログラムを更新
現在、サービス・プログラム関連のオブジェクトは以下のようになっています。 では、icafesrv のソースを修正して税率を 10 % にしてみましょう。 メニューのオプション 12 でモジュール icafesrv を作成してください。この時点でモジュール・オブジェクト内は税率が 10% になりました。しかし、図を見るとわかるように、サービス・プログラム内のモジュールは 8% のままです。 サービス・プログラム内のモジュールを最新にするには、サービス・プログラムを再作成するか、モジュールを置き換えるかのどちらかを実行します。今回はモジュールの置き換えで対応しましょう。メニューのオプション 15 を選択し、以下のように指定して実行します。 コマンド UPDSRVPGM により、サービス・プログラム内のモジュールが最新のものに置き換えられます。 では、icafe023 を実行して結果を確認してください。税率 10% で計算されて、画面表示は 2420 になっているはずです。と言いたいところですが、おそらく税率は8%のままで計算されていると思います。一度サインオフをしてジョブを終了し、再度サインオンしてからもう一度実行してください。10%で計算された結果が表示されるはずです。これについては次回、詳細をお話します。 今回の修正はサービス・プログラムのみで、 プログラムは一切修正を行っていない点に注意してください。今回は関数を使用するプログラムが一つだけでしたが、複数あったとしても関数を使用するプログラムは一切修正することなく、サービス・プログラム側の修正のみで対応可能です。ロジックの部品化をサービス・プログラムで実現し、システムの保守性を高めていきましょう。  

終わりに

いかがでしたか?今回はサービス・プログラムの基礎的な部分を説明しました。共通ロジックや、複数プログラムで重複しているロジックなどをサービス・プログラム化することで、保守性を高めるだけでなく新規プログラムの作成もより簡単に行うことができるようになります。IBM i は今後も長きに渡って使用されるものですから、システム構築や改修の際には、この保守の容易性を高めるためにサービス・プログラムの使用をぜひ検討してください。 次回もサービス・プログラムについてさらに詳細な解説を行っていく予定です。お楽しみに!
いいねと思ったらシェア
twitter
facebook
hatena
IBM i の”新”必須言語 〜FFRPG入門〜 目次を見る

この連載は…

IBM i の”新”必須言語 〜FFRPG入門〜
関連記事
【FFRPG】第一回 とりあえず作ってみよう!
【FFRPG】第一回 とりあえず作ってみよう!
【FFRPG】第七回 データベース – ネイティブ・アクセス(更新、削除プログラム)
【FFRPG】第七回 データベース – ネイティブ・アクセス(更新、削除プログラム)
【FFRPG】第三回 プログラムの基本構造
【FFRPG】第三回 プログラムの基本構造
あなたにオススメの連載
できるIBM i 温故知新編
9記事
できるIBM i 温故知新編
IBM i の”新”必須言語 〜FFRPG入門〜
14記事
IBM i の”新”必須言語 〜FFRPG入門〜
IBM i アプリの第二の柱 OSS
15記事
IBM i アプリの第二の柱 OSS
PAGE TOP