OOCOBOL こーなー


■はじめに

以前は COBOL97の愛称で親しまれていた愛称OOCOBOLは現在COBOL2000と呼ばれているそうです。
そろそろOOCOBOLもじっくり見ておかなければいけないなぁ と考えていて記憶によればISOが最新Documentをcopy代金のみで$70くらいで配っていた・・・さてどこだったかなぁぁ とWWWを徘徊していたら
『ISO/IEC CD 1.1 1989』Issued August 1,1997
なるものが転がっていたので,思わずラッキー!!とgetしてきました。この文書・・・正式な出所がなにものかはよくはわからないのですが,かぎりなくそれっぽいので信用させていただいて,自分が理解していこうかな と思っています・・・あくまで私が理解して整理したいのが目的です(笑)
これは MicrofocusのHPで見つかりました!!
http://www.microfocus.com/Standards/
の割と後ろの方に Electronic draft for downloading - new version as of 19 March 1998 というところにありますから関心のある方はGETです!!

■まずはアウトライン〜どんなものなの Object-Oriented COBOLって〜

これは現在ISOで標準化作業まっただなかの現行COBOL85の次期バージョンです。まずはISOで標準を作ってでもってANSIになって,でもってでもってJIS-X・・・となって・・・そうなると各社H/Wメーカーはこぞって準拠のコンパイラを提供して・・・本屋には猫も杓子も『OOCOBOL入門』『OOCOBOL概説』『ぜったいわかる!OOCOBOL』な〜んて本がわらわらわらっと出てくるんですよ(笑) 日本ではまだまだミッションクリティカルな部分にはCOBOLが使われていて,要員数もばかにはなりません。いままでだったらOOPといえばC++とかJAVAとかSmalltalkとかあるにはあるんですが,多勢に無勢のシステムおじさん達にはハードルが高くて(笑) やっと出てきたのがCOBOLへの適用ということになります。

ただしご注意!!当たり前の話ですがCOBOL2000ってオブジェクト指向専用のCOBOLではありません。現在のCOBOLに対する数多くの改善機能や新機能がたくさん盛り込まれています。個人的な好き嫌いは横においてもビジネス分野でのCOBOLという言語の完成度は相当高い!と言ってよいでしょう!!正確にはCOBOL2000の新機能の超目玉機能の1つがオブジェクト指向の機能なのです。

結論から言ってしまえば・・・OOPとして最先端・・・などとは言えません。最先端にするためには過去のしがらみを切り捨てる必要があります。でもCOBOLは過去のしがらみ・・・というか資産継承が大きな特徴なのです。これはOOCOBOLを導入した場合にCOBOL85の資産がそのまま使用できることを意味しています。大規模ソフトウエアというのは生産性や拡張性の高い言語が開発されたからと言っておいそれとは移行するわけにはいきません。でもOOCOBOLだったら・・・ソフトウエア資産を段階的に再構築できるのです。新規開発案件にしたって、現状ではOOAやOODができる要員や実績が十分とは言えないでしょう!!そんな状況では基幹業務にいきなり「うーぷ!!」(OOP:ObjectOrientedPrograming の発音)というわけにはいきませんね。でもCOBOL85の設計者やプログラマはうじゃうじゃいるでしょうから(笑) COBOL2000を導入してもほとんどはCOBOL85文法で記述して、「ここはオブジェクト指向でチャレンジしてみよう!!」というアプローチが取れるわけです!!やりかたを間違わなければいたれりつくせりって感じですね!これこそが標準化されている言語の強みだと言えるでしょう!!

ざっくりOOCOBOLの仕様を眺めてみるとC++やSmaltalkやEiffelに見劣りする機能があります・・・けれども勝っている機能もあります。一番は日本語化の問題です!日本のJISで制定されるCOBOLでは名前に漢字が使えて漢字でシンボリックデバッグできる環境が言語メーカーから提供されます・・・そんなのあたりまえなんですがCOBOL以外の言語では結構深刻な問題です。見劣りする部分は・・・まあ しかたがないのではないでしょうか?でも目的は生産性や拡張性をいかに向上させるか、品質を向上させるか ですから・・・OOPの先端を走ることではないはずです!!オブジェクト指向という考え方がもたらすメリットを機能として提供できればいいはずです。結論的には一通りのことができます。というか一般的なOMTやUMLを使用してOOA、OODした場合の成果物としてのオブジェクトモデルをOOCOBOLは表現することができます。だいたいできることを箇条書きにしてみましょう!!

●クラスとインスタンスの概念
●継承による差分プログラミング
●メソッドの遮蔽定義
●多態性による処理の共通化
●SELF、thisもOK

とまあ・・・なかなかなものでしょう?(笑) いやぁ制限もあるんですが今は触れないということで(笑)


SECTION.1 OOCOBOL構造の概略

■C++でのサンプル

みなさん C++くらいは知っているでしょう!!(笑)という前提をおかせてもらってクラス定義の簡単な例をもとに、それがOOCOBOLではどのようになるのかを説明します。

#include "流動性預金.h"
#include "顧客.h"
class 普通預金 :流動性預金{
private:
string 店番;
string 口座番号;
int 支払可能残高;
顧客 myCIF;
//その他の属性
public:
int 入金(int IN入金額);//流動性預金の入金をoverride(遮蔽定義)している
int 出金(int IN出金額);
//その他のメソッド
};

すこし説明しますと ここでは普通預金というクラスを定義する・・・利用者が新たに普通預金というデータ型を作ると考えてください。class というのはC++の予約語で「class から }; までがクラス定義だよ!!」という意味です。ここの例では 入金というメソッドは定義だけで実装はされてはいませんが・・・まあ細かいところは横においておきましょう。
class 普通預金 というのはクラス名が 普通預金 ということです。どうやって使うか?ですが 例えば数値や文字列だったら

int i1,i2;
string s;

とかいて 整数型(int)の変数i1とi2 文字列型(string)の変数s と理解するのと同じように

普通預金 ft1,ft2;

とかけば 普通預金型(普通預金)の変数ft1,ft2 が用意された!! ということになります。
普通預金クラスもオブジェクト指向の考えに従えば、属性と振る舞いをカプセル化したものということになります。属性(=インスタンス変数)として店番、口座番号、支払可能残高そしてちょっと変わっている顧客型のmyCIFとがあります。これら属性は private: と明示的に指定されていますので外部から直接参照することはできません。これがカプセル化であり情報隠蔽と言われる機能です。

顧客型がちょっと変わっているというのは、これがOOA/OODで言うところの HAS-A関係(集約関係) と言われているもので、顧客というクラスがどこかで定義されていて普通預金クラスではそれを部品として再利用しているということになるのです。同じように普通預金クラスも誰かほかのクラスが再利用することが考えられます。
次に振る舞い(=メソッド)ですが、入金()と出金() が定義されています。これらのメソッドは public: と指定されています。この意味は公開されていて外部から参照することができるという意味です。
入金 というメソッドのコメントに
「流動性預金の入金をoverride(遮蔽定義)している」
という記述がありますが実は 普通預金はゼロから定義しているのではなくて流動性預金というクラス定義をベースにして作成しているのです。それは

class 普通預金 :流動性預金{

の :流動性預金 というところです。ここを読んでみると
「class として定義する 普通預金 は 流動性預金 から継承して定義される」
ということになります。そこで 入金メソッドがoverrideとコメントしているのは、入金メソッドは親クラスである流動性預金で定義されているので普通預金では、入金メソッドを定義しなくても流動性預金の入金メソッドは継承してもっていることになるのですが、まあそのままでは使えない・・・と言った理由で 普通預金用の入金メソッドを定義し直す!!ということになります。で・・・出金メソッドは流動性預金では定義されていなかったことにします。なぜこんなややこしい例にするのか?と言うとOOCOBOLではメソッドを新規に定義する場合と、定義し直す(遮蔽定義)場合ではちょっとだけ書き方が違うからなんです。
最後に先頭の

#include "流動性預金.h"
#include "顧客.h"

というのは普通預金を流動性預金から継承するため、流動性預金にはどんな属性やメソッドが定義されているかをコンパイラが知る必要があるために書く必要があるのです。myCIFの顧客クラスも同様です。顧客クラスの定義は
顧客.h というヘッダファイルに定義されているということになります。
(1998/4/29)


■サンプルをOOCOBOLで書いた場合

ではさくさくっと

IDENTIFICATION DIVISION.
CLASS-ID. 普通預金 INHERITS 流動性預金.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS 流動性預金.
CLASS 顧客.

FACTORY.
PROCEDURE DIVISION.
METHOD-ID. 口座開設.
DATA DIVISION.
LINKAGE SECTION.
01 IN店番 PIC X(3).
01 IN口座番号 PIC X(7).
01 INmyCIF USAGE IS OBJECT REFERENCE 顧客.
01 RESULT PIC 9(3).
PROCEDURE DIVISION USING IN店番 IN口座番号 INmyCIF RETURNING RESULT.
口座開設-start.
EXIT METHOD.
END METHOD.
END FACTORY.

OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 INSTANCE-DATA.
03 店番
03 口座番号
03 支払可能残高
03 myCIF USAGE IS OBJECT REFERENCE 顧客.
PROCEDURE DIVISION.
METHOD-ID. 入金 OVERRIDE.
DATA DIVISION.
LINKAGE SECTION.
01 IN入金額 PIC 9(12).
01 RESULT PIC 9(3).
PROCEDURE DIVISION USING IN入金額 RETURNING RESULT.
入金-start.
EXIT METHOD.
END METHOD.

METHOD-ID. 出金.
DATA DIVISION.
LINKAGE SECTION.
01 IN出金額 PIC 9(12).
01 RESULT PIC 9(3).
PROCEDURE DIVISION USING IN出金額 RETURNING RESULT.
出金-start.
EXIT METHOD.
END METHOD.

END OBJECT.
END CLASS.

C++と比べると・・・長くなってしまっています(笑) まあ しかたがないですが 書けるということです!!
(1998/5/6)


■サンプルOOCOBOLの解説

CLASS-ID. 普通預金 INHERITS 流動性預金.

OOCOBOLでは従来のようにメインプログラムを書くこともできます。C++の例にはクラス定義はあっても void main() といったプログラムの開始点はありませんでした。OOCOBOLも同様で、CLASS-ID と書くとクラス定義だけがされているプログラムということになります。

CLASS-ID に続く 普通預金 がクラス名となります。

CLASS-ID. 普通預金.

と書けば 普通預金クラスをゼロから定義することになります。例では INHERITS とありますので 流動性預金 というクラスから継承して 普通預金 クラスを定義することになります。

REPOSITORY.
CLASS 流動性預金.
CLASS 顧客.

C++の#includeに相当します。普通預金 クラスで再利用するクラス部品をすべてREPOSITORY句で宣言します。対象となるのはHASA関係として包含するクラス部品(顧客)だけではなくISA関係の上位クラス(流動性預金)も書かなければなりません。どのクラスを部品として再利用するかをコンパイラに教えてあげなければ流動性預金がどのような主属性を定義しているのかとかどのようなメソッドがあるのかがわからず、コンパイル時に検査することができないためです。

FACTORY.
OBJECT.

CLASS-IDで始まったクラス定義は END CLASS. で終わりますが、さらにその内部は大きく FACTORYからEND FACTORY.までのFACTORY句 と OBJECTからEND OBJECTまでのOBJECT句 の2つの句から成り立っています。
OOCOBOLではクラスに関わる属性や操作の定義と インスタンス(=オブジェクト)に関わる属性や操作の定義ができます。クラスに関する部分がFACTORYで、インスタンスに関する部分がOBJECTとなります。

FACTORYではクラスに関する属性と操作ができますが、例ではクラス属性の定義がありません。もしクラス属性が必要なら

FACTORY.
DATA DIVISION.
WORKING-STARAGE SECTION.
01 普通預金口座総数 PIC 9(5).

といった感じになります。例では

FACTORY.
PROCEDURE DIVISION.

となっているのでクラスメソッドが定義されていることになります。

METHOD-ID. 口座開設.


メソッド名はMETHOD-IDで指定し、ここでは「口座開設」となります。

DATA DIVISION.
LINKAGE SECTION.
01 IN店番 PIC X(3).
01 IN口座番号 PIC X(7).
01 INmyCIF USAGE IS OBJECT REFERENCE 顧客.
01 RESULT PIC 9(3).

続くDATA DIVISIONではメソッドの引数や戻り値、口座開設メソッド内で使用される局所変数を定義します。
INmyCIFは顧客クラスのインスタンスが引数で渡されてきます。OOCOBOLではC++とは異なり顧客インスタンスそのものを埋め込むのではなく オブジェクト参照(OBJECT REFERENCE)を使用します。オブジェクト参照はオブジェクトそのものではなくオブジェクトを指し示すもの というイメージになります。オブジェクト参照として定義したからと言って顧客インスタンスが用意されているわけではありません。誰かが顧客クラスのクラスメソッドを呼び出してインスタンスを生成して、そのあとオブジェクト参照が生成されたインスタンスを指し示すように初期化する必要があります。ちょっと先走ると SET statementを使用します。

01 INmyCIF USAGE IS OBJECT REFERENCE 顧客.


OBJECT REFERENCEの後ろにクラス名として 顧客 と書かれていますが、これはINmyCIFが顧客インスタンス専用のオブジェクト参照ということです。INmyCIFを普通預金クラスや明細クラスのオブジェクト参照としては使えないように、つまり型付けを制限することができます。もしも

01 INmyCIF USAGE IS OBJECT REFERENCE.

のようにクラス名が指定されていないと、INmyCIFは任意のインスタンスのオブジェクト参照になることができるということになります。

PROCEDURE DIVISION USING IN店番 IN口座番号 INmyCIF RETURNING RESULT.

ここでは、IN店番、IN口座番号、INmyCIFが入力引数
RESULTが戻り値になります。

END METHOD.

ここまでが1つのメソッドということになります。

END FACTORY.

ここまでがFACTORYで、クラスのインスタンスやメソッドの定義が終わります。
つぎは・・・

OBJECT.にはじまって
END OBJECT.まで

ここはインスタンスに関する定義部分です。インスタンス変数とインスタンスメソッドが定義されますが、

DATA DIVISION.

いきなりDATA DIVISIONからはじまっていますが、これがインスタンス変数を記述する場所です。その9行あたり下にもDATA DIVISIONがありますがこれはメソッドの内部にあるDATA DIVISIONでメソッドの局所変数やメソッドの引数を定義するものです。今回の「入金」「出金」ではLINKAGE-SECTIONなので引数のサンプルということになります。

WORKING-STORAGE SECTION.
01 INSTANCE-DATA.
03 店番
03 口座番号
03 支払可能残高
03 myCIF USAGE IS OBJECT REFERENCE 顧客.

インスタンス変数としては店番、口座番号、支払可能残高、myCIFが定義されています。これは普通預金クラスのインスタンスごとにこれらのインスタンス変数領域が確保されているということになるのです。

PROCEDURE DIVISION.

これがインスタンスメソッドの定義の始まりです。

METHOD-ID. 入金 OVERRIDE.

ここのOVERRIDEとは上位クラスにあたる流動性預金にも入金メソッドが定義されていて、流動性預金から派生して作成した普通預金クラスはなにも定義しなくても流動性預金の入金メソッドを持っていることにはなります・・・が、たとえば処理が違うといった理由で普通預金用に作り直す(これを遮へい定義と言います)ためにOVERRIDEと書いているのです。
あと出金メソッドも定義されています。

END CLASS.

ここでCLASS定義が終わるというわけです。
簡単ですね・・・ちょっと長いですけど(笑)

(1998/5/17)


■プログラム構造

COBOLには6つのコンパイルグループが用意されます。

・program-prototype
・function-prototype
・program-definition
・function-definition
・class-definition
・interface-definition

で、通常見かけるCOBOLはprogram-definitionとなり、OOCOBOLの場合はclass-definitionということになります。6つはそれぞれコンパイル単位として別物ですからオブジェクトの部品(クラス定義)とその部品を使用するクライアントプログラム(通常見かけるCOBOL)とは別物になります。
C++のようにclass定義とmain()とが共存する書き方は出来ないことになります。

例)class定義とmain()が共存
class OrdinaryAccount{
}
void main(){
OrdinaryAccount myAccount;
}

またC++では1つのソースファイルに任意の数のclass定義が可能ですが、OOCOBOLでは1つのプログラムに1つのclass定義しか出来ません。
厳密に言うと・・・上記コンパイルグループは

[{program-definitionやclass-definitionなど}・・・]

と定義されているので1つのファイルには、クラス定義を3つとかmainに相当するクライアントプログラムを2つとかをあわせて記述できそうです。

説明上、program-definitionのことをプログラム定義COBOL、class-definitionのことをクラス定義COBOLと呼ぶことにすると、
プログラム定義COBOLのプログラム構造

[IDENTIFICATION DIVISION.]
PROGRAM-ID. program-name-1 [AS literal-1][IS {[COMMON] PROGRAM].
・・・


となり
クラス定義COBOLのプログラム構造

[IDENTIFICATION DIVISION.]
CLASS-ID. class-name-1 [AS literal-1]
[INHERITS FROM {class-name-2}・・・]
[USING {parameter-name-1}・・・].
[environment-division]
[[IDENTIFICATION DIVISION.]
FACTORY.
[options-paragraph]
[environment-division]
[data-division]
[PROCEDURE DIVISION.
[method-definition]・・・]
END FACTORY.
[[IDENTIFICATION DIVISION.]
OBJECT.
[options-paragraph]
[environment-division]
[data-division]
[PROCEDURE DIVISION.
[method-definition]・・・]
END OBJECT.
END CLASS class-name-1.


となります。

[IDENTIFICATION DIVISION.]

の[]は省略できるということです。省略してしまっていきなり

CLASS-ID. class-name-1

から書き始めてもよいということになります。ですから構文上省略できないOOC0BOLの構造は

CLASS-ID. class-name-1.
FACTORY.
END FACTORY.
OBJECT.
END OBJECT.
END CLASS class-name-1.

となるはずです。ここで明白ですがPROCEDURE DIVISIONが存在していません(正確には、FACTORYやOBJECTの段落には記述できます)からプログラム実行開始点となるメインは存在しないということになります。
次はクラス定義の構造をDIVISIONごと概観していきましょう。

□IDENTIFICATION DIVISION.

●PROGRAM-IDの代わりにCLASS-IDを使用する。ということはC++のようにクラス定義とmain()とが同一プログラムには記述できないということです。

●継承の場合はINHERITSで指定します。多重継承は可能で、クラス名の指定順序は同一のメソッド名が表れた場合に先に記述してあるクラスのものが有効となります。

CLASS-ID. 普通預金 INHERITS 流動性預金 .


□ENVIRONMENT DIVISION.
クラスを再利用する場合はCONFIGURATION SECTIONのREPOSITORY段落にクラスを記述します。HASA関係のクラスだけではなくISA関係としての親クラスも指定する必要があります。

REPOSITORY.
CLASS 流動性預金.
CLASS 顧客 IS 'CIF'.


顧客がinternal nameで、CIF がexternal nameとなります。
通常のCOBOLやオブジェクトのクライアントアプリケーションがクラス定義COBOLのオブジェクトを使用したい場合もREPOSITORY段落を記述しなければなりません。このRIPOSITORYはIR(InterfaceRepository)と呼ばれています。

□DATA DIVISION.
●定義できる場所は3か所あります。

・FACTORY段落のDATA DIVISION
ここで定義されるとクラス変数となります。
・OBJECT段落のDATA DIVISION
ここで定義されるとインスタンス変数となります。
・METHOD定義の中のDATA DIVISION
ここで定義されるとメソッドの中での局所変数となります。
○LOCAL-STORAGE SECTION.
永続機能はない
○WORKING-STORAGE SECTION.
永続機能あり:メソッドをEXITしても値は残っています。再度同じメソッドにENTERした場合前回の値が残っています。
○LINKAGE SECTION.
引数や戻り値を定義しておきます。


●クラスの再利用

01 W普通預金 USAGE IS OBJECT REFERENCE.
01 W当座預金 USAGE IS OBJECT REFERENCE 当座預金.

データ項目としてオブジェクトを利用する場合、OOCOBOLではOBJECT REFERENCEを使用します。C++のようにインスタンスそのものを直接埋め込んで使うことはできません。オブジェクトはFACTORYのnewメソッドのようなインスタンス生成メソッドによって作成しておき、そのインスタンスをさすようにOBJECT REFERENCEを初期化します。
OBJECT REFERENCEにはtypedとuntypedがあり、typedでは指定されたクラスだけではなく下位のクラスを指し示すことが可能です。untypedの場合はどのようなクラスでも指し示すことができます。


□PROCEDURE DIVISION.
●メソッド定義
OOCOBOLのメソッドはC++で言うところのvirtualかつpublicとなります。

METHOD-ID. 入金 OVERRIDE.

上位クラスのメソッドを書き換える場合(遮蔽定義)は、OVERRIDEを指定します。また各メソッドはメソッド名(例では「入金」)と自分自身のDATA DIVISIONとPROCEDURE DIVISIONを持っています。DATA DIVISIONは当該メソッドの局所変数を定義します。メソッドへの引数や戻り値もここで定義します。

●オブジェクト参照(OBJECT REFERENCE)の設定方法
大きくは2通りあります。
(1)FACTORYのnewメソッドの戻り値として設定する。

01 W普通 USAGE IS OBJECT REFERENCE 普通預金.
・・・
INVOKE 普通預金 "new" RETURNING W普通.

W普通には生成された普通預金クラスのインスタンスへのオブジェクト参照が代入されています。
(2)SET文で設定する。

・SET A TO B.

オブジェクト参照Aをオブジェクト参照Bが指しているものに設定する

・SET A TO NULL.

オブジェクト参照AをNULLに設定する(どこも指し示していないようにする)

・SET A TO SELF.

オブジェクト参照Aを自己を参照するように設定する

●MessagePassing request方法(オブジェクト参照に利用の仕方)
2つの記述方法があります。

・INVOKE 普通預金 "入金" USING PARAM.
・普通預金::入金(PARAM).

invokeではメソッド名情報を変数に格納することも可能です。リテラルであればstatic linkとなり、datanameであればdynamic linkとなります。

■以上