ソフトウェア開発方法論
開発の全体像(開発プロセスと開発方法論)
ソフトウエア開発手法と聞いて、皆さまは何を思い浮かべるでしょうか?この連載を読んでいる方々は、少なからず、ソフトウエア開発に携わっていらっしゃるかと思いますが、それでも、思い浮かべる内容は十人十色かと思います。本連載では、ソフトウエアの開発手法のうち、「開発プロセス」という切り口に焦点を当て、その基本的な部分を紹介していきたいと思います。
開発プロセスとは、一言で表すと「ソフトウエア開発をどう進めるか」を体系化したもの、となります。この開発プロセスは、主に、ソフトウエアにより構成されるシステム全体(または開発プロジェクト)を対象としていて、システムの規模や複雑さが増大するにつれ、必要性や重要性が認識され始めました。最近では、システムのみならず、プロジェクトにかかわるメンバーのモチベーション等を視野に入れているものもあり、この辺りは、プロジェクト管理手法との関連も深くなってきています。
さて、早速、代表的な開発プロセスの1つ、ウォーターフォール・モデルの……と行きたいところですが、その前に開発方法論を紹介したいと思います。ここで言う開発方法論とは、一言で表すと「ソフトウエアをどう作るか」を体系化したもの、を指します(図1-1)。
構造化技法(構造化プログラミング)
ではまず、開発方法論として、構造化技法を取り上げます。1960年代には、開発されるソフトウエアの大規模化、複雑化により、ソフトウエアの開発効率化や品質向上に対するニーズが高まっていました。これに対する解決策として提唱されたのが、構造化定理を基にした構造化技法でした。
----------------------------------------
構造化定理:プログラムは、「順次・選択・繰り返し」の3つの基本構造を組み合わせた、1つの入り口と1つの出口を持つ形式で記述できる。
----------------------------------------
上記「順次・選択・繰り返し」とは、例えば、Javaの場合、下記と考えて問題ありません。
・順次:上から順番に実行すること
・選択:if文
・繰り返し:for文
つまり、この構造化定理では、あるプログラムは、if文やfor文を組み合わせたサブルーチンとして記述できる、と言っているのです。
この構造化技法とは、プログラムをいくつかの単位に分け、あるプログラムから別のプログラム(サブルーチン)を呼び出す構造とすることにより、プログラムを理解しやすい(開発しやすい)構造とすることと言えます。
これは、システム全体では複雑な処理に見えても、細かく分割していけば単純な処理の積み重ねにできる、というものです(図1-2)。現在では、後ほど説明するオブジェクト指向が主流になっていますが、構造化技法の考え方は、オブジェクト指向の考え方と矛盾するものではなく、むしろ、根本的に違いはないので、基本的で重要な考え方と言えます。
ここまで構造化技法の説明をしてきましたが、「複雑な処理を簡単な処理に分割する」とは、何かピンときませんか?そうです、「モジュール化」に似てますね。構造化技法は、現在でもソフトウエアの品質基準として使われるモジュール化を促進するものと言うことができます。では次に、モジュール化の評価基準について見ていきましょう。
モジュール化の評価基準(凝集度と結合度)
ここでは、プログラムがどれほどモジュール化されているか、という評価基準として、「凝集度(強度とも言います)」と「結合度」について説明します。この評価基準は、オブジェクト指向においても同じように適用できるため、非常に重要と言えます。
凝集度とは、1つのモジュール内の関連性についての尺度で、以下のように分類されます(凝集度の高い順で1→7となっており、凝集度が高いほうが望ましいとされる)。
1)機能的
1つの機能を実行するモジュール。
2)情報的(逐次的)
同じデータやリソースを処理する複数の機能をまとめたモジュール。まとめられた機能は関連性が高く、実行順が固定であり、それらを分割してもメリットがないようなケース。
3)連絡的(通信的)
情報的凝集度と似ているが、まとめられた機能の実行順が固定ではないようなケース。
4)手順的
ある実行順で処理される複数機能をまとめたモジュール。まとめられた機能の関連性が弱いケース。
5)時間的(一時的)
初期処理や終了処理のように、特定のタイミングで実行する機能をまとめたモジュール。まとめられた機能間にあまり関連はない。よくあるケースのように思えるが、モジュールの再利用性が悪く、同じようなモジュールが複数作られるケースを生むため、凝集度が低いと見なされる。
6)論理的
関連した複数の機能を持ち、実行時におけるモジュール呼び出しの引数等により、モジュール内の機能が決定されるケース。
7)暗号的(偶発的)
モジュールは、プログラムを単純に分割しただけで、複数の機能を持つが、機能間にまったく関連はない。
結合度とは、モジュール間の関連性についての尺度で、以下のように分類されます(結合度の低い順に1→6となり、結合度が低いほうが望ましいとされる)。
1)データ結合
相手モジュールをブラックボックスとして扱うことができ、必要なデータだけを相手モジュールとやり取りする。相手モジュールとは、やり取りするデータ以外の関連はない。
2)スタンプ結合
相手モジュールに、必要なデータ以外もやり取りするケース。例えば、必要な要素を含む構造体(やクラス等)のすべてを相手モジュールに渡す場合を指す。
3)制御結合
相手モジュールの処理を制御するようなパラメータを相手モジュールに渡すケース。凝集度の論理的凝集度に相当する。
4)外部結合
モジュール間で必要なデータを他モジュールから参照できるようにし、相手モジュールと共有するケース。
5)共通結合
モジュール間で必要なデータを、共通領域に定義し、他モジュールと共有するケース。
6)内容結合
絶対アドレス等を用いて、自分のデータを、直接相手モジュールに渡して処理をさせるケース。
オブジェクト指向(オブジェクト指向設計、オブジェクト指向プログラミング)
構造化技法がシステム全体の機能を細かく分割していく、という考え方だったのに対して、システム全体の機能を、オブジェクト間の相互作用で表現する、すなわち「オブジェクトに分割していく」という考え方が、オブジェクト指向と言えます。ここで言うオブジェクトとは、データと処理を持つモジュールという風に考えてください。
このオブジェクト指向は、ますます大規模化、複雑化するソフトウエアの開発効率を上げるには部品化によるモジュールの再利用化が不可欠であり、それは構造化技法だけでは実現できない、という背景があり普及していきました。そのため、オブジェクト指向の研究が進むにつれて、オブジェクト指向に対応したプログラミング言語が次々と誕生しました。C++やJava、今をときめく(?)Rubyはもちろんのこと、COBOLも最近の拡張により、オブジェクト指向に対応しています。
では、オブジェクト指向とは、何でしょうか。簡単に表現するのは難しいのですが、オブジェクト指向には下記のような特徴があります(図2)。
・クラスとオブジェクト
同じ特性(データや処理内容)を持つ複数のオブジェクトを纏(まと)めて抽象化し、クラスとして定義する。クラスは枠組みであり、実行時にはクラスから実体化されたオブジェクトが処理を行う。
・カプセル化
クラスの内部を外部(ほかのクラス)から隠ぺいすること。隠ぺいするのは、クラスの持つデータや処理内容である。これにより結合度が低くなる。
・メッセージパッシング
オブジェクトがほかのオブジェクトに処理を実行させるためには、メッセージを送るだけで良い、ということ。これは、ほかのオブジェクトのデータ構造や処理内容を知らずに、指示を出すだけで良いということを指している。
・継承
クラスの特性(データや処理内容)を、ほかのクラスから引き継ぐこと。これにより、あるクラスを簡単に拡張できるようになる。ただし、継承を使った場合、継承元クラスと継承先クラスとの結合度が高くなってしまうことに注意が必要である。
・ポリモフィズム(多態性、多相性、多様性)
他オブジェクトからの操作に対して、実際に呼び出されるオブジェクトに従って、処理内容が決まるということ。これは、プログラム作成時点では、どのような処理が行われるかは決定せず、実行時のオブジェクトによって処理内容を切り替えることができる、ということを意味する。
オブジェクト指向にこれらの特徴があることがおわかりいただけたと思います。では、どうなれば「より良いオブジェクト指向」なのでしょうか。評価が難しいのも「オブジェクト指向は難しい」と言われるゆえんではないでしょうか。そこで、オブジェクト指向の評価基準について見ていきたいと思います。
オブジェクト指向の評価基準
オブジェクト指向の評価基準として、Robert C. Martinによる、オブジェクト指向設計の原則(Principles of OOD)について説明します。
1)SRP:単一責任の原則(Single Responsibility Principle)
クラスを変更する理由は1つでなくてはならない。また、クラスの責務(役割)は1つでなければならない。
2)OCP:オープン・クローズド原則(Open-Closed Principle)
機能拡張をソース追加で行うことができ、既存ソースを修正できないようになっているべきである。
3)LSP:リスコフの置換原則(Liskov Substitution Principle)
あるクラスで正しく動作する場合、そのクラスの派生クラスでも正しく動作すべきである。
4)DIP:依存関係逆転の原則(Dependency Inversion Principle)
呼び出し側のモジュールは呼び出される側のモジュールに依存してはならない。どちらも抽象クラスに依存すべきである。
5)ISP:インターフェース分離の原則(Interface Segregation Principle)
使用していないインターフェースに依存してはならない(使用されないメソッドを公開すべきではない)。
このように、オブジェクト指向に関して、これまでにもさまざまなノウハウが蓄積されています。このノウハウが形式知としてまとまっているものの代表が、上記原則であり、デザインパターンである、と言えるでしょう。デザインパターンと一口に言っても、いろいろなものが発表されていますが、その中でも特に有名なのは、GoF(The Gang Of Four)のデザインパターンでしょう。「車輪の再発明」を行わない意味でも目を通しておくことをお勧めします。
少し話がそれますが、ここで、UMLについて紹介したいと思います。オブジェクト指向の開発方法論として、1990年代ごろには、Booch法やOMT法等、いくつかのものが提唱されていました。現在では、それらの開発方法論自体は、UP(Unified Process)やRUP(Rational Unified Process)等の開発プロセスとしてまとめられています。
しかし、これら開発方法論の大きな功績として忘れてはならないのが、UML(Unified Modeling Language)の誕生でしょう。UMLは、それまでバラバラの表記法を使用していた夫々の開発方法論のノウハウを統合して策定されました。このUMLが、オブジェクト指向を普及させる推進力となったのは間違いないでしょう。
プロジェクトの現場では
構造化技法とオブジェクト指向、両者の住み分けはどのようになっているのでしょうか。実際の現場における使われ方について見ていきたいと思います。これまでも散々言われてきたことですが、「オブジェクト指向は難しい」ことは事実でしょう。つまり、スキルレベルがある程度以上の開発者でないと、オブジェクト指向を使いこなすことはできません。
他方、開発プロジェクトは、オブジェクト指向を使いこなせるだけのスキルレベルを持つ開発者のみで構成されているとは限りません。むしろ、そのようなケースのほうが少ないでしょう。特に大規模プロジェクトになるとなおさらです。この観点から言うと、すべての開発者がオブジェクト指向を駆使して開発するケースは少ないと言って良いでしょう。
もともと、オブジェクト指向の目的の1つが再利用化だったこともあり、オブジェクト指向は再利用されるようなプログラムを作成する際に大活躍します。その再利用されるプログラムがフレームワークであり、プロジェクトの共通基盤であるのです。すなわち、既に開発されているフレームワークを特定プロジェクト向けに拡張したり、プロジェクト固有の共通基盤を設計開発するのは、オブジェクト指向を使いこなせるスキルレベルを持つ一部の開発者が行います(図3)。
ほかの大多数の開発者は、それらフレームワークや共通基盤をルール通りに使いこなすことが多いでしょう。そういう意味では、フレームワークや共通基盤が整備されたプロジェクトにおいては、敷かれたレール通りにコーディングするだけなので、プログラマーとしては「つまらない」かもしれませんね(ただし、オブジェクト指向を理解していないで良いという訳ではないので、その点はお間違いなく)。
最後に、オブジェクト指向と似た言葉として、アスペクト指向という言葉を紹介しておきます。現状ではアスペクト指向は、開発方法論というよりも、1つの考え方と見なしたほうが良く、さらに言うと、オブジェクト指向のすき間を補完する目的であると言えます。オブジェクト指向ではうまく分類できない特性(複数のクラスで共通に持つ機能等)を「アスペクト」と見なし、クラスの切り口とは別にプログラミングできる仕組みが整備されつつあります。
さて、次回は、いよいよ開発プロセスを紹介していきたいと思います。お楽しみに!