2012.1.16
デビッド・シレイ著

RPGプログラムの複雑さに対処する

私たちの生活が複雑さで満ち溢れていることは誰もが知っています。いつの日か今から何万世紀後に鳥たちが地球を征服して私たちの化石と化した遺物を調べてみたら、私たちの社会はそれ自体の複雑さのために破壊されたのであるという結論にゆっくりとではありますが必然的に達するでしょう。このことを指し示す場所は私たちが毎日取り扱っているRPGプログラムを置いて他にはありません。複雑なプログラムは書くのもテストするのもデバッグするのも保守するのも簡素なプログラムに比べるとずっと難しいものです。したがって、複雑なプログラムはその価値と不釣合いに時間と労力を搾り取ってしまうこともあるのです。

プログラムの複雑さ、特にRPGプログラムの複雑さの原因を理解し、複雑さによってもたらされる問題を軽減させる戦略を採用することで、より信頼性が高く一貫性のある正しいプログラムを作成しながら、テストとデバッグのフェーズにおける時間を節約できるようになります。

設計そして構造から始める

わかっています。複雑さとプログラムの保守性については誰もが不安に感じていると口をそろえますが、心配を口にするばかりで行動を起こす人はほとんどいません。また皆さんが何をもって簡素と考えているかを聞くと驚かされることが多々あります。悲しいことに簡素さということ自体が私たちの本質の中にはないというのが事実なのです。私たちは期限に追われる種族であり、私たちが作り出すものの美に喜びを見出す種族ではないのです。それはとても悲しいことではあるのですが。昔のプログラマの金言に「プログラムは1度しか書かれないが千回変更される。特に最初から大きくて複雑なものであればなおさらである。」というのがあります。その複雑さが将来の期限に対して大惨事をもたらすのです。

真の意味での簡素さを求めるには、プログラムとその目的を注意深く見て一貫性のあるすなわち簡素な構造を混乱全体に対して適用する必要があります。どこから始めるべきかと言えばその設計であり、できればコードを書く前に作成した設計から始めるべきです。

System iのプログラマのほとんどにとっては「設計」は禁句であり巨大なシステムを構築する場合に実施するものなのです。重さと数と体積に基づいて価格を設定するような顧客の価格決定機能を強化するだけなら設計など必要ないのです。これはしごくあたりまえのように思えます。確かに顧客クラスとカテゴリの組み合わせなどといった例外は多数あり、おそらく要件も時と共に変化していきますが、そんなことはたいしたことではありません。プロジェクトを3ヶ月で完了させなければならないのです。そして2週間以内に開発室に戻されて修正を始めることになるのです。

実際のところ設計とは「実施したほうがよい」ものではなく、どんなプロジェクトでも実施すべきものなのです。設計を厳密に行っている時間がないということは、とりもなおさずプロジェクトのことを最優先に熟考している時間がないといっているのと同じことになります。ウェブ・プロジェクトを遂行するための方法論なら何でも構いませんので頭に思い浮かべてみてください。どんなウェブ・プロジェクトもスタートラインでは同じです。まずは(ユーザーと徹底的に話すことで)ユーザーの環境をよく知ることが最初のステップです。次のステップでは紙を取り出すかAxureやiPlotzなどといったプログラムを使用してワイヤーフレームの設計の概略を作成します。

優れた設計を作成するには、以下のことを上手にするためにたくさんの時間を費やす必要はありません。まず、ユーザーの目標と環境を本当に理解すること、特にしっくりこない要件が重要です。ユーザーは救いようもないほど楽観的で複雑さについてうまく言い紛らわすことがほとんどです。ユーザーの話を聞き、質問をしてユーザーの実際のニーズとユーザーがやりたいことを区別します。その分野についてどれだけ知っていても、新しい状況、規則、戦略的方向性がいつも浮かび上がってきますので、ユーザーの真の要件を理解するようにしてください。

二番目は将来のことを考えるということです。ビジネス状況に変化があったら何が起こるのかとか、アプリケーションを中断させたり制限を加えたりするものは何かについて静かに熟考する時間を取ってください。設計の中に制約や難所がないかどうか探してみてください。それらを公開するか、あるいはシステムをそれ自身から守ることができますか。

そして三番目としては、アプリケーションの構造やアプリケーションを構成するすべての部分の本当の意味を展開します。構造とは画面やCLコマンド、あるいは本当に大きなプログラムなどのことではありません。この要件を満たす論理的な考えを探し、その考えをコード・モジュールとしてそれぞれ構築していくのです。最終的には、設計とは物理的なプロセスをシステムの文脈においてモデル化し、コードの実体と一対一に対応する関連した一連の論理的な考えに分割していくための方法なのです。

コードの小型化

設計を前もって行う(意識的に細かなレベルの考えを用意しておく)ことで、容易に取り扱うことのできる一連の思考単位を作ることができます。このそれぞれの思考単位は1つの完成した考えであり、コードになったときに後述するガイドラインにほぼ自然に沿ったものになります。コードを小さなモジュールに分割することをコードの小型化(SCC: Small Chunk Coding)と言います。

そう、「1つにまとめれば十分なのになぜいくつもの小さなモジュールに分けるのか。1は11より小さいのに。」とお尋ねになるかもしれませんね。確かにおっしゃる通りです。しかし問題の一部は可視性なのです。脳は受け入れなければならないことが一目ですべて見えるときに最も効率的に作用するのです。したがってコードの部分が小さければ小さいほどその全体を可視化して理解しやすくなるのです。もう一点は精神的なものです。ページを前後にめくらなければならない場合、何が書いてあるのかわからなくなりやすいのです。これはちょうど文の主語が3頁、動詞が6頁、形容詞はすべて10頁に書いてある文を理解しようとするのとまったく同じです。

もちろん、簡素さはモジュールを小さくすることの充分な理由であるわけですが、もう1つの理由があります。それは単体テストです。単体テストはコードの小さな部分をさまざまな条件下で実行するプロセスのことです。単体テストはJavaの世界では一般的ですがSystem iのRPGで単体テストを実施する人はあまりいません。1,800行の一枚岩からなるコードを単体テストするのは難しいのです。

つまり肝心なのは実際に求められていることを実現し、その要求の背景にある中核となるあるいは補助的な論理的な考えを自然と分離できるような設計をする必要があるということです。これができればそうした論理的な考えをSSCを使って簡素にしかも明確にコードとして記述することができます。ではそうした小さなモジュールをどのようにコード化するのかについて考えてみましょう。

書かれている以上のことを読む

「コードとは書かれている以上のことを読むものである」という格言は簡素化のプロセスの確信をついている言葉です。コードを理解するにはコードを読むことができなければなりません。そしてコードを変更する前にはコードを理解しなければなりません(少なくとも理屈の上ではそうです)。本質的にはコードは記事のようなものです。コードを書くことは重要ですが、書いたコードを誰も読むことができなかったり意味を理解することができなかったりするのであれば、コードを書いたことの意義はほとんどありません。

不幸なことに私たちは皆、典型的なRPGプログラムのほとんどを読むのに必要な、コードを断片的に読むことが得意になっています。ポジショナルなRPGは線形でも時系列的でもありませんので、要素が意味を成すように要素を読む順番を反転させる必要があり、頭の中で翻訳しなければならないような速記形式で書かれていて、コードの塊を深く読み込んで情報を解読したり変数のスコープがどこで終わるのかを見つけなければならなかったりします。しかし断片的にコードを読むことができるからといって、それが良いことであるとか、そうすれば最速で作業が進められるというわけではありません。

他に何も信ずるところがないのであれば、理解可能なコードを書きたければたとえどんなに小さなモジュールでも読みやすいコードを書かなければならない、ということを信じてください。線形でスコープの始まりと終わりがはっきりしていて、必要最小限のデータ要素を含むコードを書く必要があります。考えを比較的少ない文字数で整理でき、他の事に時間を割けるのであればそれが最良です。再チェックする必要のあることや覚えておかなければならないことが少なければ少ないほど良いのです。読みやすいRPGのコードを書くには以下に述べる簡単で基本的な考え方を忠実に守る必要があります。

常に/Freeを使用する

プログラムを簡素化する方法のリストの最初に来るのは/Freeを使用すること、そして/Freeだけを使用すること、です。これについては疑問の余地はありません。残念なことに私たちは従来のRPGをまだ使っているという場合がほとんどです。それはなぜでしょう。それはRPGに慣れてしまっているからです。ではなぜRPGをやめなければならないのでしょうか。それには次の4つの理由があります。

  1. /Freeでは字下げ(インデント)ができる
    RPGのベテランの中には字下げを一蹴する人がたくさんいます。字下げだって、と。ENDIF文はどこで始まってどこで終わるかがわかるのになぜ字下げなのか、と。それは字下げをするとIF文がどこで始まってどこで終わるかがずっとわかりやすくなるからです。3頁にわたる何重にも入れ子構造になったIF文を読んでいろいろな節がどこで始まってどこで終わるのかを読み解かなければならないときが時々あるのです。
  2. /Freeで読みやすさが向上し構文の複雑さが減る
    /Freeはポジショナルではないので、動詞が節の最後に来るドイツ語のように、読まずにすむようにしなければなりません。コードは左から右に向かって読みます。ここでも根っからのRPGプログラマは、そんなことはたいしたことではない、と言うかもしれません。そうした人たちは逆から読むことに熟達しており/Freeの簡素な構文など必要としないのです。しかし実際にRPG IVのプログラマが書いたような小説を読みたいと思いますか。読みやすければ読みやすいほど良いに決まっているのです。
  3. /Freeでコードの要素をより近くに配置できます
    /Freeには同じ種類に属する項目をグループ化する機能がありますが、ポジショナルなRPGではこれらの項目は別々になります。たとえば、普通のRPGの中にあるChain文を読んでいるとすると、KLIST仕様に戻ってキー・フィールドが何であるかを調べなければなりません。/Freeでは、キー・フィールドをChain文の中で指定することができますので、あちこち行ったり来たりする必要がありません。/Freeを使っていただくと、コードの要素がプログラムの他の部分ではなくあるべきところにあるようになっているという例が他にもたくさんあるのに気づくでしょう。
  4. /FreeはRPGが向かう方向である
    この傾向により、特に/Freeはとても簡単に使用できるので/Freeに乗り換えない理由は見当たりません。

入れ子のレベルを制限する

入れ子を使用するとIFやDOなどといった制御文をより上位の文に組み込むことができますので、コーディングのパラダイムとしては強力で必要なものです。しかし他のパラダイムでもそうですが、誤用されることがあるようです。入れ子のレベルが多すぎるとプログラムは生き地獄のようになります。しかしレベルがいくつになれば多すぎるといえるのでしょうか。それは状況や他の複雑度の要因に依存しますが、一般的にはレベルが3つあるいは4つ以上になると混乱を生じやすくなるようです。

残念なことに、このガイドラインを越えるレベルの入れ子を使わざるを得ないビジネス課題が多いようです。となると次に問題となるのは、(1) 本当に5段階の入れ子が必要なのか(本当にそんなに複雑なのか)ということと、(2) 本当に必要ならばこの特別なロジックをどうするのか、ということです。

このような状況に対処するときによくするのが論理的な複合質問です。すなわち、複数の異なる変数に関係することです。たとえば、顧客のクラスをIF文で始めるのですが、その値に応じて顧客のカテゴリを尋ねたり製品コードさえも尋ねたりすることになってしまいます。このような場合には1つの巨大な入れ子になったIF文を、各データ要素がサブルーチンやサブプロシージャなどのコードの別のセクション中のIF文になっている複数の小さなIF文で置き換える方がまったく理にかなっています。

さて、「プロシージャ」ではなく「サブルーチン」という言葉をたった今使いましたが、言論統制に走る人もいるかもしれません。「サブルーチンという言葉は2度と使わないように」と言うべきだったかもしれませんが、そんなに大きな害があるようには思えないのです。確かにプロシージャを使ったほうが良いですし、常にこれを使ったほうが良いでしょう。しかし小さなコードのモジュールがあるのだったらサブルーチンという単語を使っても構わないでしょう。プロシージャとサブルーチンの違いによってプログラムが格好いいか悪いかが決まるのではありません。いずれにしてもサブルーチンあるいはプロシージャを小さくしておくことが重要です。

データ要素のスコープを制限する

任意のデータ要素が参照されるコードの範囲を制限する必要があります。つまり、そのデータ要素は1度だけ参照されますか、それとも50回参照されますか、ということです。その50回はプログラム全体に渡って分散していますか、それとも1つのサブプロシージャやサブルーチン内にとどめられていますか、ということです。一般的にはデータ要素がプログラム全体にわたって分散していればいるほど、そのプログラムは複雑なものになります。目指すところは各データ要素が参照される範囲を1つの小さなプロシージャ内に制限することです。

ここに本当の意味で設計が関係してくるのです。アプリケーションを一連の小さな論理的な考えにうまく分割できれば、任意のデータ要素がハコベのように一面に広がることのないようにする道のりの半ばまで来たようなものです。プロセスの一部で、1つ以上の論理的な考え(あるいはプロシージャ)に登場する要素がデータにないかチェックしてみてください。データ要素がプログラム全体に広がるという、データの連結によって生じる問題を見くびってはいけません。

IF文を簡素化する

これは入れ子の問題と似ていますが、単一レベルのIF文においても影響があるという点において少し意味が違います。問題なのはほとんどすべてのビジネス規則に、プログラムがあることを実行するのかしないのかを決めるのを支援する条件ロジックであるIF文が1つ以上含まれるということです。しかもyesかnoの条件で決められるような簡単なケースはほとんどなく、非常に多くの選択肢が含まれることがほとんどです。

複雑なIF文を使用するのは複数の条件を1つの文にカプセル化する方が読みやすさが向上するからです。ただし、間違ったコーディングをすると複雑なロジックに精神的についていけなくなります。特にAND、OR、NOTなどのオプションを組み合わせるとなおさらです。構文規則を遵守するのはもちろんですが、わかりやすくコーディングしないと、どの節がANDやORに関係しているのかを解読しないといけないようでは混乱を招きます。これにさらにNOT条件を混ぜてしまうと混乱度は倍増します。このジレンマから抜け出す方法はフリー・フォーマットの字下げや入れ子を使用して論理節のグループ化や関係を明確に記述して時間をかけて複雑な条件をコードすることです。

プロトタイプ化されたプロシージャを使用する

サブルーチンに対して強く反対しているわけではないと上で告白しましたが、プロトタイプ化されたプロシージャを使うべきですから、使えるときは必ず使ってください。これは何を意味するのでしょうか。まず、プロシージャは分離されたモジュールであるかメイン・プログラムの一部であるコードの単位です。私は(プロシージャを元のプログラムの中に組み込むのではなく)アプリケーションをそれぞれ異なるプログラムに分割するのが好きです。そうすることで、すべてを含んだ大きなマスター・プログラムを1つ用意するのではなく、メインの制御プログラムからすべて呼び出される小さな一連のプログラムを用意するのです。そして大きなプログラムに比べて小さなプログラムの方が読みやすく、理解しやすいのです。

2番目に、EXSRではなくopcode呼び出しを使用してメインのモジュールからプロシージャにアクセスするということです。そして/Freeを使うことにしていますから、CALLP opcode (プロトタイプ化された呼び出し)を使用します。プロトタイプ化することにはいくつか利点があります。1つ目は速度です。CALLPは従来のCALL opcodeよりもずっと高速で高効率です。2つ目として、CALLPはプログラムを実行するときではなくコンパイルするときに呼び出し側と呼び出される側の間でパラメータの不一致(パラメータの数とパラメータの型の)を見つけます。

プロトタイプ化をしたことがないのであれば、是非始めてください。パラメータを定義してプログラムを呼び出すにはD仕様をほんの少しだけ余計に記述するだけです。さらに、プロトタイプ化すると古いPLISTや厄介なCALL opcodeの構文を使用しなくてすみます。すばらしいものですので是非お試しください。

新しい考え方を生み出す

プログラムの複雑さを制限するということは、ヒント集ではなく考え方であり、画面上にコードを書き始める前に設計を通じて考え始めるということなのです。これで何を達成しようとしているのでしょうか。最終的な目標は、どのような個々の論理的考え方の組み合わせから成り立っていますか。プログラムとサブプロシージャを使ってそうした考え方をどのように構造化したいですか。

RPGプログラムのレベルでは、簡素さとは知る必要のあることを一目で見るということです。お気に入りの本を読むようにコードを読むことができるということです(クロスワード・パズルよりもおそらくヘンリー・ジェームスのほうがまだ読みやすいでしょう)。一番重要なのは、簡素さとは何もしなくて成し得るものではないということです。複雑さは放っておいてもやってきます。簡素さについて努力しなければならないのです。しかし今日の世界ではこれ以上に価値のある努力が思い浮かびません。

ページトップ

ボタン