オブジェクト指向言語じゃない、COBOLで依存性逆転の法則を実践してみる

# 動機

オブジェクト指向言語じゃない、COBOLで依存性逆転の法則を実践してみて、疎結合の理解を深める。
プログラミング言語の制約が色々あるので、イマイチですが(COBOLには動的に依存性を注入するような仕組みがなく、コンパイル＆リンク時に具象モジュールを結合しなければいけないなど)、疎結合な内部設計の発想を広げるということで。

[Wikipedia 依存性逆転の法則](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87)

``` 
この原則に従うとソフトウェアの振る舞いを定義する上位レベルのモジュールから下位レベルモジュールへの従来の依存関係は逆転し、
結果として下位レベルモジュールの実装の詳細から上位レベルモジュールを独立に保つことができるようになる。
A. 上位レベルのモジュールは下位レベルのモジュールに依存すべきではない。両方とも抽象に依存すべきである。
B. 抽象は具象に依存してはならない。具象が抽象に依存すべきである。
この上位レベル、下位レベルの両方とも抽象に依存しなければならないと言う要求により、この設計原理は一部の人々のオブジェクト指向プログラミングに対する考え方を反転させる。
```

# シナリオ1
CSV形式で商品名と価格を出力するCOBOLアプリ、PRICE-APPを作ります。

```bash:
APPLE,12345
```

# 従来のレイヤーパターン
上位レベルのプログラム(PRICE-APP)は下位レベルのプログラム(SHOW-PRICE-CSV)をCALL。

```
ソースファイル名=PRICE-APP.cbl(PROGRAM-ID=PRICE-APP)
 ---> ソースファイル名=SHOW-PRICE-CSV.cbl(PROGRAM-ID=SHOW-PRICE-CSV)
```

# 依存性逆転パターン

COBOLには抽象クラスがないので、どうするか？ ``PROGRAM-ID``を抽象クラスに見立て、ソースファイル名を具象クラスに見立てます。

```
ソースファイル名=PRICE-APP.cbl(PROGRAM-ID=PRICE-APP) ---> PROGRAM-ID=SHOW-PRICE
                                                                     ^
                                                                     |
                                                     ソースファイル名=SHOW-PRICE-CSV.cbl
```

図を書き換えているだけで、コードは何も変わっていないように見えますが、
``PROGRAM-ID``を抽象クラス名に見立て``SHOW-PRICE``に書き換えて(``CSV``を取り外し抽象的な名前にしてみました)、</br>
``ソースファイル名``を具象クラス名に見立てて、``SHOW-PRICE-CSV.cbl``にして俯瞰してみると、
``PROGRAM-ID``さえ同じなら、具象クラスは何でも良くて、差し替え可能と見えてきます。

# シナリオ2
PRICE-APPの変更しないで、JSON形式で商品名と価格を出力するCOBOLアプリを作ります。

```bash:
{"NAME":"APPLE","PRICE":12345}
```

# 呼び出し元のPRICE-APPを変更しないで、JSON形式のプログラムに差し替える

``PROGRAM-ID``とその引数を変えずに、ソースファイル名を別の名前にしてJSON形式で出力するプログラムを作り、
コンパイル＆リンク時にそのソースに差し替えればJSON形式の出力アプリができます。

```
ソースファイル名=PRICE-APP.cbl(PROGRAM-ID=PRICE-APP) ---> PROGRAM-ID=SHOW-PRICE
                                                                     ^
                                                                     |
                                                     ソースファイル名=SHOW-PRICE-JSON.cbl
```
# COBOLソース

下記dockerコンテナの``/home/cobol``直下に次の3つのファイルを作ります。

```cobol:PRICE-APP.cbl
000000 IDENTIFICATION DIVISION.
000000 PROGRAM-ID. PRICE-APP.
000000*--------------------------------------------------------------
000000 ENVIRONMENT DIVISION.
000000*--------------------------------------------------------------
000000 DATA DIVISION.
000000*--------------------------------------------------------------
000000 WORKING-STORAGE SECTION.
000000 01    POUT         SYNC.
000000   03  POUTNAME     PIC X(5) VALUE 'APPLE'.
000000   03  POUTPRICE    PIC 9(5) VALUE 12345.
000000*--------------------------------------------------------------
000000 PROCEDURE DIVISION.   
000000 A00-MAIN SECTION.
000000 A00-010.
000000     CALL 'SHOW-PRICE' USING POUT.
000000 A00-EXIT.
000000     MOVE ZERO TO RETURN-CODE.
000000	   GOBACK.
```

```cobol:SHOW-PRICE-CSV.cbl
000000 IDENTIFICATION DIVISION.
000000 PROGRAM-ID. SHOW-PRICE.
000000*--------------------------------------------------------------
000000 ENVIRONMENT DIVISION.
000000*--------------------------------------------------------------
000000 DATA DIVISION.
000000*--------------------------------------------------------------
000000 WORKING-STORAGE SECTION.
000000*--------------------------------------------------------------
000000 LINKAGE SECTION.
000000 01    POUT         SYNC.
000000   03  POUTNAME     PIC X(5).
000000   03  POUTPRICE    PIC 9(5).
000000*--------------------------------------------------------------
000000 PROCEDURE DIVISION USING POUT.
000000*--------------------------------------------------------------
000000 A00-MAIN SECTION.
000000 A00-010.
000000   DISPLAY POUTNAME ',' POUTPRICE.
000000 A00-EXIT.
000000   MOVE ZERO TO RETURN-CODE.
000000	 GOBACK.
```

```cobol:SHOW-PRICE-JSON.cbl
000000 IDENTIFICATION DIVISION.
000000 PROGRAM-ID. SHOW-PRICE.
000000*--------------------------------------------------------------
000000 ENVIRONMENT DIVISION.
000000*--------------------------------------------------------------
000000 DATA DIVISION.
000000*--------------------------------------------------------------
000000 WORKING-STORAGE SECTION.
000000*--------------------------------------------------------------
000000 LINKAGE SECTION.
000000 01    POUT         SYNC.
000000   03  POUTNAME     PIC X(5).
000000   03  POUTPRICE    PIC 9(5).
000000*--------------------------------------------------------------
000000 PROCEDURE DIVISION USING POUT.
000000*--------------------------------------------------------------
000000 A00-MAIN SECTION.
000000 A00-010.
000000   DISPLAY '{"NAME":"' POUTNAME '","PRICE":' POUTPRICE '}'.
000000 A00-EXIT.
000000   MOVE ZERO TO RETURN-CODE.
000000	 GOBACK.
```
# 実行環境
COBOLのちょいお試しのdockerコンテナイメージを公開してくださっているので、こちらを活用しました。

[プログラムちょい替え（4）COBOLを40年ぶりにうごかしてみた:dockerでcobol, docker(81)](https://qiita.com/kaizen_nagoya/items/9d9a216ce1b7b05dbb43)

## コンテナイメージの取得と実行

```bash:
docker run -v /home/madilloar/prj/cobol:/home/cobol -it kaizenjapan/cobol /bin/bash
```

## コンテナイメージの起動と停止

```bash:
docker start boring_mendeleev
docker stop boring_mendeleev
```

## コンテナ内に入る

```bash:
docker exec -it --user root boring_mendeleev bash -p
```

# コンパイル＆リンク、及び実行
## CSV形式プログラムのコンパイル＆リンク

```bash
cd /home/cobol

cobc -x -o PRICE-APP PRICE-APP.cbl SHOW-PRICE-CSV.cbl

./PRICE-APP

APPLE,12345
```

## JSON形式プログラムのコンパイル＆リンク

```bash
cd /home/cobol

cobc -x -o PRICE-APP PRICE-APP.cbl SHOW-PRICE-JSON.cbl

./PRICE-APP

{"NAME":"APPLE","PRICE":12345}
```