目次
具体的なシステム開発実習として、会議室予約システムを開発する。
複雑なものを一度に理解しようとしても、なかなか難しい。
子供のとき、手に取ったおもちゃがどういう仕組みで動いているのか知りたくて、分解してみた、という経験がある人も多いと思う。
人は、複雑なものを理解するとき、それを小さく分解していく。 理解できるまで小さくなったら、理解できた小さいものを再び組み合わせて大きくしていく。 この過程を経ることにより、複雑なシステムを理解できるようになる。
分解といっても、ただやみくもに小さくしていくのでは、統一性に欠けるし、元に戻らないかもしれない。 おもちゃを分解したはいいが、結局元に戻らず壊れてしまった、という経験もまた、あるかもしれない。 それでも中がわかったのなら良いのかもしれないが、せっかく仕組みを理解したおもちゃでもう遊ぶことはできない。
複雑なシステムの多くは「階層構造」をしていることが知られている。(階層構造になっていない複雑なシステムも存在する)
この階層構造には、2種類が存在する。 Part of階層と、Is a階層である。
自動車というのは、複雑なシステムである。非常に多くの部品が集まって1台の自動車を構成している。前節の特徴の1.によれば、自動車システムは階層の形を取っているはずである。
では、どのような階層を構成するだろうか?
下の図は、自動車を構成する部品によって階層構造を作ってみたものである。
この図で行けば、自動車はインテリア系、電気系、駆動系、シャーシ系の4つの部分からなり、さらにインテリア系はシート、ルーム灯、オーディオの3つの部分からなっている。
このような「部分からなる」という階層関係は、part of 階層と呼ばれる。
これに対して、たとえばエンジンについて考えれば、
のような図を考えることができる。これも階層構造をあらわしているが、エンジンがガソリンエンジンとディーゼルエンジンの2つの部分から構成されているというわけではなく、エンジンにはガソリンエンジンとディーゼルエンジンの2種類があることを表す。言い換えれば、ガソリンエンジンとディーゼルエンジンは両方ともエンジンである、ということを意味する。
このような「~である」という階層関係は、is a 階層と呼ばれる。
part of 階層は、システムの論理的な構成を表し、is a 階層は、システムを構成する要素間の継承関係を表している。part of 階層のことをオブジェクト構造、is a 階層のことをクラス構造と呼ぶこともある。
そしてこれら2つの構造をまとめてシステムのアーキテクチャと呼ぶ。
このようにして、複雑なシステムのアーキテクチャを構築することが、そのシステムを理解するための方法となる。
Dijkstra は次のように述べている。
「複雑さを支配するための技術は古代から知られていた。それは分割統治である。」
すなわち、複雑なシステムを設計するときは、システムを徐々に小さな部分に分解していき、その個々を独立の設計することが重要である。人間の認知の許容限界以下にまで分解ですることで、われわれはそのシステムを理解することが可能となる。
分割の方法には2通りがある。機能分割とオブジェクト指向分割である。
機能分割とは、その名の通り、システムが必要としている機能に着目して分割を進める。
従来のソフトウェア開発では、おおむねこの方法でシステムの分割が行われていた。この方法は機能、すなわちアルゴリズムを主体として考えている。このため、データとアルゴリズムの間の関係が希薄であり、しばしばデータとアルゴリズムの整合性を欠く実装を伴う可能性がある。
オブジェクト指向分割とは、システムの持つべき機能ではなく、システム内に現れる鍵となる登場物に基づいて分割する方法である。つまり、分割の主体は「~する」という動詞ではなく、「操作者」や「データベース」といった名詞になる。そして、それらの登場物の間にメッセージと呼ばれる動詞が飛び交うことになる。
機能分割とオブジェクト指向分割とでは、同じシステムを対象としていても、出来上がる図はまったく違った形になる。では、どちらの図を用いるほうが正しい分割なのだろうか?
答えから言えば、どちらも正しいし、どちらも重要である。
機能分割は出来事の順番(アルゴリズム)に着目しているし、オブジェクト指向分割は動作を引き起こす主体、あるいは操作が行われる対象に着目している。つまり2つの図は、同じシステムを異なる観点から見ているだけである。
しかし、現代の主流になっている考え方では、オブジェクト指向分割を行ったほうが良いとされている。それは以下の理由による。
ソフトウェアには次のようなライフサイクルが存在する。
このライフサイクルをモデル化したものに、以下の3つがある。
ソフトウェアの開発プロセスのモデルとして最初に提案されたものがウォーターフォールモデルである(1970年代)。このモデルは、下の図に示すように、滝から水が落ちるように工程が上から下へと実行されることが特徴である。各工程において、次の工程へ渡すために生成される成果物をプロダクトと呼ぶ。
上の例では、要求分析の結果として仕様書が生成され、その仕様書を基にしてシステム設計が行われ、その結果として基本仕様書が生成される。さらに、基本仕様書を基にしてプログラム設計が行われ、結果として詳細仕様書が生成される。
このように、各工程での作業の結果としてのプロダクトを残していくことによって、プロジェクトの開発履歴が明文化された形で残ることになり、下工程のテストや保守が容易になる。
ウォーターフォールモデルでは、1つの工程を完了しない限り、次の工程へ進むことはできない。各工程には、生成されたプロダクトを検査する方法と、各工程をいつまでに完了するかの計画(マイルストーン)が付随する。
このモデルは非常に分かりやすいモデルであるが、さまざまなプロジェクトに応用されていくにつれて、実際のソフトウェア開発の手順とは一致しないことが明らかになってきた。特に下流工程においては、たとえばプログラムが完成するまでテストは行われない、などといったことは、実際の開発現場ではありえない。実際の現場では、少し開発してはテストして、それを踏み台にしてさらに開発を進めるといった形態がとられる場合が多い。
また、実際のソフトウェア開発においては、さまざまな箇所で繰り返しが発生するが、ウォーターフォールモデルでは複数の工程にまたがった繰り返しを記述できない。たとえば、開発工程が進んでいき、コーディング段階になって要求分析に不十分な点が発見されたとしたら、それまでに得られたプロダクト(仕様書、基本設計書、詳細設計書)をすべて破棄してから要求分析をやり直し、再度すべてのプロダクトを作り直さなければならない。現実にこのような開発を行うことは効率が悪すぎる。
このようなウォーターフォールモデルの問題点は、システム全体に対する分析や設計を一括して行うところに原因がある。すなわち、最初にシステム全体を見通して問題領域を分析し、それが完成してから全体を設計するというやり方は、ソフトウェアの開発には向かないのである。
このようなウォーターフォールモデルの欠点を補い、繰り返し処理を含んだプロセスを表現可能としたモデルが、1988年に Boehm によって提案されたスパイラルモデルである。
スパイラルモデルでは、最初から複雑な要求を全部満たすソフトウェアを作成することはしない。まず全体の要求を分析し、より本質的なもの、あるいは単純で実現が容易なもののみに限定した要求のみを抽出する。そしてこれに対する仕様書1を作成し、この仕様書に基づいて設計・コーディングの工程へと進む。
工程によって生成されるプロダクトも、システム全体の要求を完全に満たすものではなく、大まかな動作や事象の流れを把握するためのもの(プロトタイプ)となる。プロトタイプにおいては、システムの機能は完全には動作しない。たとえば、インベーダゲームのプロトタイプで、自分の宇宙船の動作を目的としていた場合、敵のインベーダは現れないかもしれない。宇宙船が完全に正しく動作することを確認した後、次はインベーダを動かすための工程に入ることになる。
スパイラルモデルは、小さいプロジェクトから初めて、分析・設計・コーディング・テストを繰り返しつつ、少しずつシステム構築していく方法であり、現実のソフトウェア開発プロセスに合った、非常に有効な工程管理手法である。実際、多くのソフトウェアはこのようなプロセスを経て開発されていると思われる。
しかし、このモデルは、ソフトウェア開発者にとっては、たいへん効率よく開発を進めることができるわかりやすいモデルを提供したが、開発全体を管理する管理者にとっては、徐々に構築されていくソフトウェアシステムが本当に正しく(要求を満たして)作られているのかを把握しにくい、という問題があった。すなわち、システム開発全体に対してのリスクを管理しにくいという点が、スパイラルモデルの問題点として指摘されていた。
Booch によると、失敗するソフトウェアプロジェクトには、欠如している次の2つの特徴があるという。
強力なアーキテクチャの見通しの存在 うまく管理された反復的かつ斬進的な開発ライフサイクルの適用 前者は開発プロセス全体を巨視的な視点から見て、プロセスの進め方を把握することであり、マクロプロセスと呼ばれる。マクロプロセスは従来のウォーターフォールモデルに関連が強く、開発工程全体を管理するためのプロセスであるといえる。
一方後者は、システムを徐々に繰り返して構築していくことを意味しており、マイクロプロセスと呼ばれる。マイクロプロセスはまさにスパイラルモデルそのものであり、システムに対してさまざまな改良を加えつつ、少しずつ完成に近づけていく。この作業は現場のソフトウェアエンジニアになじみの方法であり、エンジニアの創造性や技術革新が反映されやすい。
しかしながら、これら2つの特徴は相反する要求であり、実際に適用する上においては、これら2つの要求をいかに融合させ調和させていくかが問題になる。すなわち、「開発者は、マイクロプロセスの非形式性とマクロプロセスの形式性の間の釣り合いをとらなければならないという点が重要である(Booch)」。
このような要求に対して出された1つの解答が、統一プロセス(United Process:UP)である。
UP では、工程管理は大きく2つのレベルに分けられる。1つは管理者が開発過程全体を見渡して大域的な観点から管理を行うマクロプロセスであり、もう1つは開発現場のエンジニアが少しずつソフトウェアの改良、進化を行うことができるマイクロプロセスである。
マイクロプロセスはいわゆるスパイラルモデルを採用しており、与えられた要求を、より小さな要求に分解して繰り返し開発を行う。この過程で、エンジニアの創造性や技術革新性が発揮される。
このプロセスは、「要求分析」、「システム分析」、「設計」、「実装」、「テスト」の5段階の分かれており、この流れを繰り返すことでスパイラル的に工程を進めていく。
また、マクロプロセスはフェーズと呼ばれる4つの工程を管理者の立場から管理するものであり、形としてはウォーターフォールモデルに似ている。4つのフェーズは、それぞれ次のような意味を持つ。
このような段階的なフェーズを経て、管理者が開発に伴うリスクをできるだけ軽減しつつ、高い開発効率を目指す工程管理手法である。
オブジェクト指向開発によってソフトウェアを作成するということは、言い換えればモデルを作成していくと言うことができる。したがって、モデルという考え方を身につけることが、オブジェクト指向開発をマスターすることの第一歩となる。
モデルとは、何らかの実体があって、それを縮小(あるいは拡大)して細かい部分は省いて本質的なところだけを抽出して形成した形のことである。プラモデル、ファッションモデル、分子モデルなど、世の中にはさまざまなモデルが存在する。
ソフトウェアのプログラムを作らなければならないとき、まず「やるべきこと」が与えられる。これは、外部(顧客)からの要求として与えられる場合もあるし、自分でやりたいことを考える場合もある。
この「やるべきこと」から、即座にそれを実現するためのプログラムが(ちょうど、プログラミング教科書にある簡単な例題のように)頭に浮かぶならば、何の問題もない。しかし、現実問題として、このようなことはまずない。(赤線)
そこで、「やるべきこと」を一度、モデルという抽象的な形に変換し、このモデルを中継点として、プログラムの実装に接続するという過程が必要となる。(黄線)
しかし、「やるべきこと」は本来、依頼主(顧客)が持っているものであり、開発者側は「やるべきこと」について詳しくない場合が多い。このようなとき、「やるべきこと」をすぐプログラムに直結させるようなモデルとして表現することははなはだ困難となる。
そこで、いったん「やるべきことのモデル」を作成し、このモデルをどのようにして実現するかという「やり方のモデル」に変換してから、プログラムにするという方法が取られる。(青線)
この過程は、前に見た分析と設計の過程に対応する。すなわち、与えられた問題を分析することが「やるべきことのモデル」に、その分析に基づいて設計することが「やり方のモデル」に対応している。
統一プロセス(UP)のマイクロプロセスの工程は、「要求分析」、「システム分析」、「設計」、「実装」、「テスト」の5つに分けられていた。
工程モデルを考える場合、これらそれぞれの工程に対して、対応するモデルが作成される。
上の図は、UPにおいて、各工程に対応するモデルを表している。
要求分析工程はユースケースモデルとして表現される。ユースケースとは、そのシステムを使うときにどのように働くかを、場面に応じて検討することであり、UML のユースケース図とユースケース記述によって表す。このモデルは、システムを利用するユーザー側の視点に立って、システムの挙動を分析する。
システム分析工程は分析モデルとして表現される。このモデルでは、前工程のユースケースモデルからオブジェクトを抽出し、クラスの作成とクラス間の関係を明らかにする。このモデルは、ユーザー側と開発者側の橋渡しをする役目がある。
設計工程は設計モデルとして表現される。このモデルは、実装を前提として前工程の分析モデルを詳細化する。このモデルではかなりプログラムよりの表現が使用され、次の実装モデルへの橋渡しとなる。
実装工程は実装モデルとして表現される。このモデルはいわゆるプログラムコードである。
最後のテスト工程はテストモデルとして表現される。テストモデルは、テスト仕様とテストプログラムからなるが、実はこのモデルが最も大変である。テストは、システム全体をブラックボックスと見て、外部要求を満足するよう動作するかをテストするブラックボックステストと、分析モデルと設計モデルから導き出された、個々のクラスの動作をテストするホワイトボックステストに大きく分けられる。
UPによるソフトウェア開発を、問題領域と解決領域に分けて考えることができる。
問題領域とは、依頼主(顧客)からの要求が、依頼主にも分かるような形で表現されている領域であり、この領域内にある限り、それがプログラムに直接結びつかない。この領域では、モデルは平易な言葉(日本語など)で表現され、主に依頼主と開発者との間で、作成するシステムに関する意思の疎通を図ることが目的となる。
問題領域においては、システムが使用される現実世界とやるべきことを考慮に入れてユースケースモデルを作成する。ユースケースモデルは問題領域にあるため、自然言語を用いてシステムの利用者にも理解できるような記述で作成される。
問題領域と解決領域とを結ぶのが分析モデルである。このモデルは利用者にも分かるような言葉で作られるが、その内容はオブジェクトとクラスの抽出と分析であり、半分以上は解決領域の中に踏み込む形となる。
そして、設計モデルと実装モデルは完全にプログラミング言語を意識したものとなる。
UPに基づいたオブジェクト指向開発は、次の4つの大きな技術の集大成として成り立っている。
Smalltalk や C++ 、Java といったオブジェクト指向をサポートした言語の登場で、オブジェクト指向プログラミングが一気に広まった。しかし、これらの言語はコーディングを目的とした技術であり、つまり、解決領域に対する技術である。したがって、オブジェクト指向プログラミング言語を使ってプログラムを組んだからといって、オブジェクト指向開発を行っていることにはならない。
問題領域から一貫してオブジェクト指向で分析・設計を進めていかなれば、全体としてオブジェクト指向開発はできない。
つまり、オブジェクト指向プログラミングは、オブジェクト指向開発を行う上で、解決領域において必須の技術ではあるが、それだけでは不足である。
オブジェクト指向設計とは、実装、すなわちオブジェクト指向プログラミングを前提として、クラス階層、メッセージ通信等の技術を駆使して、オブジェクト指向的な実装にスムーズにつなげられるような設計を行う技術である。
したがって、これも明らかに解決領域の技術だということになる。
オブジェクト指向設計の代表的な方法として、G.Booch の Booch 法がある。
現実世界の情報を、クラスを使って分類しモデル化するのがオブジェクト指向分析である。ここにはプログラミングと直接関係するような制約はない。特にプログラムを作ろうとする場合でなくとも、オブジェクト指向分析は有効に働く。
したがって、これは問題領域を扱う技術ということになる。
オブジェクト指向分析の代表的な方法として J.Ranbaugh の OMT(Object Modeling Technique) があげられる。
I.Jacobson によって1992年に提案された OOSE (Object Oriented Software Engineering) は、ユースケース分析と呼ばれるシナリオを起点とした問題分析方法を採用した。この分析方法が、それまで別々に行われていたオブジェクト指向分析とオブジェクト指向設計を結びつける役割を果たした。
ユースケース分析は、UPの最初に行われる問題分析であり、問題領域に属している。
ユースケース分析の結果に基づいて、ロバストネス分析(これはUMLには含まれていない)と呼ばれる手法で分析を行うことにより、問題領域から解決領域への移行がスムーズに行なえる。
オブジェクト指向開発とは、単にオブジェクト指向プログラミング言語を使ってプログラムを作成することではない。問題の分析から設計、実装にいたるまで、一貫してオブジェクト指向を用いて行うことが重要である。
UPにおいては、各工程に対してモデルを対応付け、前工程で作成されたモデルに基づいて次の工程のモデルを作成するという方法で、工程間の移行がスムーズに行われるようになっている。
UML は Unified Modeling Language(統一モデリング言語)の略であり、1990年代に群雄割拠していたオブジェクト指向分析・設計方法論の統一をはかって、1994年頃に Ratinal Software 社の Grady Booch と Jim Rumbaugh が活動を開始したことに端を発する。その後、方法論そのものを統一することは困難であるという見解から、まずは方法論で使用されるモデルの表記法を統一しようという方向に変化した。
その結果、1997年に、オブジェクト指向業界最大の団体である OMG(Object Management Group) によって UML1.1 が標準仕様として採択された。2003年3月段階での最新版は UML1.5 であり、このページはこれに準拠している。2015年7月時点での最新版はUML2.5である。
モデリング言語として、OMGのお墨付きで登場したUMLは、ソフトウェア開発の救世主であるかのような受け入れ方をされ、多くのソフトウェア開発プロジェクトがUMLを採用した。OMGが進めているUML技術者の資格認定等も、その傾向に拍車をかけている。UMLを使わないプロジェクトは旧式開発とみなされ、逆にUMLを使えば開発効率は飛躍的に向上するという認識が多くなされている。
しかし、
UMLは描き方の基本を決めているだけ
である。
「どのようにして描くのか」や、「どのようにプログラムコードと関連付けるのか」については、何も決められていない。
したがって、UMLを使えば魔法のようにソフトウェアが作られるなどということは、決してない。
「工程管理されたソフトウェア開発方法」
を修得することこそ目的である。そのためにオブジェクト指向分析と設計の技法について説明していくが、その一環としてUMLについても触れる。UML記法の背後にあるオブジェクト指向的な考え方を身につけることが重要である。
UMLは、大雑把に言うと以下の9種類の図である。(UML2.0以降は13種類)
分析・設計の各段階において、これらの図をたくさん描くことで、問題を整理し、コード化へスムーズに移行することがUMLの目的である。
しかし、実際に9種類の図をすべて駆使して分析・設計を行わなくても、ソフトウェア開発は可能である。このページでは、上の9つの図の中で、特に重要な図であると考えられる以下の3つについて説明を行う。なお、ここでは各図の概要だけを述べ、具体的な例は実際問題に適用しつつ示すことにする。
ユースケース図(Use Case Diagram)は、システムがどのように機能するべきか(ユースケース)、およびその外部環境(アクター)を表す図である。システムを使用するエンドユーザーの視点からそのシステムを見ることができる。そのため、エンドユーザーや利用領域の専門家とコミュニケートして、要求に対する相互の理解を保障することが可能となる。
つまり、ユースケース図は、システムの外部と内部との境界をはっきりさせる図であるということができる。
上の図はユースケース図の例を示している。
ユースケース図には、「アクター」と呼ばれる部外者と、「ユースケース」と呼ばれるシステムの持つ機能が登場する。
シーケンス図 (Sequence Diagram) は、システムに登場するオブジェクト間の相互作用を時系列に沿って並べて表現した図である。シーケンス図を使用すると、ユースケースを実現するのに必要なオブジェクトの集合と、そのやりとりを明確に表現できる。オブジェクト間でやり取りされるメッセージを時間順にひとつずつ記述できるため、シナリオと対応させて具体的な内容を示すのに便利である。
上の図はシーケンス図の例を示している。
シーケンス図には、上部に「オブジェクト」を持つ「タイムライン」と呼ばれる縦線と、その上に縦長の長方形で表された「アクティベーション」、さらにアクティベーション間の「メッセージ」によって構成される。
メッセージには同期型と非同期型とがある。通常の場合、送信されるメッセージは同期型である場合が多い。同期型メッセージは、呼び出されたオブジェクトでの処理が終わると必ず元のオブジェクトに復帰する。このとき特にメッセージを返さない場合は、復帰メッセージの矢印は省略できる。
同期メッセージは、プログラム言語における手続き呼び出しを表現するのに用いられる。
クラス図 (Class Diagram) は、対象領域やシステムの静的な構造を表す図であり、「クラス」と「クラス間の静的な関係 (Relationship)」を図に表現したものである。
クラス図は分析段階でも設計段階でも利用されるが、分析段階のクラス図は利用者の立場に立って問題領域を表すように書かれる。一方、開発段階におけるクラス図は開発者の視点から、より詳細化された形で書かれる。
上の図はクラス図の例を示している。
クラス図は、長方形の中に名前や属性、操作等を書き込んだ「クラス」と、クラス間をつなぐ「関係」によって構成される。関係にはさらに「関連」、「集約」、「汎化」など、いくつかの種類がある。また、設計段階になると、関係の両端に「ロール」や「多重度」を書き入れる場合もある。


UML の9つの図のうち、よく使われる3つに限っておおまかな説明を行った。
わずか3つの図についてでさえ、きわめて複雑な規則が定められていることが分かると思う。
UML は基本的に図を描くことでモデルを記述していくグラフィカル言語であるから、その文法に当たるものは図の描き方であり、形である。したがって、ある程度細かく記述方式が定められているのは仕方がない。
しかし、それにしても、ここに示された決まりを厳格に守って図を描いていくことは容易ではないと、誰もが思うだろう。しかも、UML は図の形は規定していても、具体的な描き方までは示していない。つまり、与えられた問題をUML の9つの図の形にしていく方法については、ほとんど何も決められていないのである。
したがって、UML を使ってソフトウェア開発を行おうとすると、まずはたくさんの例題を参考にして、試行錯誤で図を作っていき、それを繰り返すことでノウハウを蓄積するしかない。UML では標準的な記述の形が決まっているだけでも、参考にする多くの例題が利用できるので「まし」だと思う。
この章では、フリーのモデリング開発環境である UMLet を使用してUML図の描き方を練習してみよう。
UMLetは単純な入力方式を持つオープンソースのUMLツールである。UML図をすばやく作成し、esp,pdf,jpg,svg フォーマットの図やクリップボードなどに出力したり、Eclipseと図を共有したり、新しくカスタムUML要素を作成したりできる。
UMLetはスタンドアロンとしてもEclipseのプラグインとしても実行でき、Windows,OS X およびLinux上で動作する。
UMLetはここからダウンロードできる。
※UMLet13.2は日本語入力にバグがある。12.2を使うと良い。→最新版の13.3では直っているようだ。
また、特にインストールに必要はなく、ダウンロードしたファイルを解凍するだけである。全部で10MB程度なので、USBメモリーに入れておいても良い。また、このソフト自体Javaで作られており、実行にはJavaの実行環境が必要である。
基本的には右側にあるパレットから部品を選び、左側のダイアグラムに追加し編集する。
操作法は、一般的な描画ツールとは少し違っている。
以下にいくつかのUML図を示す。同じ図をUMLetを使って描いてみよう。