戻る

Java と構造化プログラミング

COBOL全盛のメインフレーム時代、ソフトウェアの開発手法への要求が最初に高まったのが、1960年代後半から1970年代と言われています。コンピュータが普及し、適用範囲が広がり、開発効率とソフトウェア品質の向上が求められたことが背景となっています。この頃に開発されたのが、構造化プログラミングと構造化設計です。

Javaはオブジェクト指向言語ですが、その基本的な発想には、構造化プログラミングの考え方が含まれています。ここでは、極簡単に構造化プログラミングについてまとめておきます。

構造化定理

全てのプログラムは、一つの入口と出口を持つ形(適正プログラム)に等価変形可能であり、適正プログラムでは任意のアルゴリズムを次の三つの基本的な制御構造で記述できます。

順次(Sequence)
Javaでは、セミコロン区切りの命令(ステートメント)を列挙した構造。
選択(Selection)
条件式の結果に応じて実行するブロックを変更する条件分岐構造。Javaでは、if文、switch文、try文をサポート。
繰り返し(Iteration)
ブロック内部を繰り返し実行するループ構造。Javaでは、do文、while文、for文をサポート。JDK 5.0では拡張for文を追加。

※ JDK 5.0の拡張for文(Enhanced for Loop)は、他の言語におけるforeachに当たるもので、コレクションのイタレーションや配列の要素で繰り返すfor文の略記法です。

コンピュータ制御(コントロール)は、上から順番にこれらの制御構造に従って命令を読み込み、実行していきます。これを逐次制御方式と呼びます。

言ってしまえば、Javaにとっては、構造化定理に従わないコードを記述する構造は用意されていません。

しかし、これが念頭に無いと、ラベルと繰り返しの脱出や例外処理などを駆使して、それに類することをしてしまいます。その結果、次に説明する、モジュール強度(凝集度)が低くモジュール結合度が高くなってしまうかもしれません。トリッキーなことをやろうとすれば、大概のことはできてしまいますが、それが望ましいかどうかは全く別の問題です。

例外処理、break, return は GOTO 文の一形態だと言えるかも知れません。繰り返しをラベルと break で脱出するよりも、繰り返し終了条件で脱出した方が見通しが良くなりますし、制御構造を例外処理で実現するのはコストの高い方法です。

構造化定理と構造化プログラミング

構造化定理(Structure Theorem)は、1960年代後半に数学者ベーム (Corrado Böhm) とヤコビーニ (Giuseppe Jacopini) によって証明されました。その後、ダイクストラ(Edger W. Dijkstra)の「GOTO文は有害である」(Go To Statement Considered Harmful)とする論文によって構造化プログラミングが方向付けられました。

構造化設計

構造化設計では、全体のコードを自分が扱える範囲にまで分割して考えます。一般に、ソースコードは長いほどLOC (Line of Code)単位のバグの個数が増えます。Javaで分割を考えるのは、パッケージ、クラス、メソッドなどです。

巨大なシステムの場合は、サブシステムに分割し、それをモジュールに分割します。長大な処理であれば、幾つかの処理に分割して実装します。モジュールの定義は、プロシージャ指向言語の場合は、単体でコンパイルできる最小単位ですが、Javaの場合は、単体テストの最小単位であるクラスになります。

任意のプログラムが適正プログラムに修正可能であるということは、その内部の任意の部分が、適正プログラムである部分に変更可能だということです。

プログラムを作るときには、まず、粗い粒度のモジュール間の関係に注目して設計します。Javaの場合は、ユースケース図やクラス図が該当します。クラス間、オブジェクト間の関係が書けたら、次に、それらのインタフェースとなるデータとメソッドを定義します。最後に、そのメソッドを助けるための、詳細な内部ロジックを設計します。これを段階的詳細化と呼びます。

メソッド内部では、粒度の粗い上位ブロックから詳細な下位ブロックに向かって考えて、部分部分を短いブロックの階層構造で捉えます。

長大なメソッドは短いメソッドに分割します。最上位のブロックをメソッドとすると、メソッド自身が適正プログラムです。繰り返し現れる部分や処理が大きい部分、特定のデータに依存している部分を切り出して、適正プログラムの形に変更することで、別のメソッドにして外に出します。

ソースコードを読むときは、制御構造とブロック階層を意識しながら、スコープから外れたシンボルを忘れるようにすることが重要です。人間の頭で、それほど多くのシンボルを意識し続けることが困難なためです。一般に、スコープが狭い変数は考慮事項が少ないので安全です。逆に、スコープが広い変数はメモリ上に長時間存在し続けるのでメモリ領域を圧迫し、アクセス可能なコードが増えるので考慮事項も増えてバグの温床となりがちです。

モジュール粒度

モジュール強度(凝集度)が高いとは、扱うデータに対して固有の処理がまとまっていることを意味します。Javaの場合は、オブジェクトのデータであるメンバ変数の処理としてメソッドが存在し、それらの塊として、オブジェクトのデータがメソッドによってカプセル化していることです。

モジュール結合度が低いとは、内部構造の変化が他方に影響を及ぼさないことを意味します。Javaの場合は、publicインタフェースになるメソッドのシグネチャが変更されなければ、privateなメンバやメソッド内部ロジックが変更されても、他方に影響を及ぼさないことです。

モジュール強度を高めてモジュール結合度を低くすることは、生産性と品質の向上、再利用の促進、保守容易性の向上の観点で、Javaを含めた全てのプログラムで共通して採用すべき重要な指針です。

モジュールの粒度

オブジェクト/コンポーネントの粒度を高くすることは、コードの再利用の促進/安定性の向上/保守性の向上の観点で望ましいことです。逆の場合は、そのままでは再利用できず、テストしにくく、問題が発見しにくく、問題が発生したときに原因がわかりづらく、実装の変更が広範囲に渡ります。

モジュール強度の向上と結合度の低下は設計によって実現されます。このとき、クラス図やロバストネス図を描いてみて、クラス/オブジェクトのレイヤリングを分析することが有効です。

強度と結合度

モジュール強度(凝集度)を高めるには、本質とは関係ないものは外出しにして、データに対する処理別にクラス分割します。オブジェクトは、一つの責務だけを担うように設計し、メソッドは、特定のデータに対する処理として実装します。

モジュール結合度を低くするには、オブジェクト同士がメソッド呼び出しだけで関連し、データはメソッド引数/戻り値でやり取りするようにします。

staticなpublicフィールドを介して複数のメソッドが関係したり、共有オブジェクトに対する入出力を頻繁に行っている場合は要注意です。Webアプリケーションの場合は、アプリケーションスコープやセッションスコープのような、リクエスト以上に長持ちするオブジェクトを共有して更新する場合に注意が必要です。

粒度を高めるチップス

オブジェクトの粒度を高くするためのチップスとして、次のような手法があります。

緊密な関係にある二つのオブジェクトを分離するために、間に別のオブジェクトをはさんで仮想化するという手法は、いろいろな場面で使うことができます。具体例としては、GUIのリスナー/コンテナ/レイアウトマネージャや、ビジネスロジックを実装したクラスを、コントローラやプレゼンテーション層から分離するためのラッパークラスなどで見られます。

戻る

Copyright 2004 SUGAI, Manabu. All rights reserved.
SEO [PR] !uO z[y[WJ Cu