ハッカー倫理に準拠した人工知能のアーキテクチャ設計

スポンサーリンク

派生問題:オブジェクト指向は如何にして可能になるのか

RUP機能は、拡張と再利用が可能なパッケージとしての開発プロセスを提供すると共に、反復型アーキテクチャ中心設計漸進的ユースケース駆動型開発の区別を導入することによって、短いタイムスパンとリードタイムで<結果>を少しずつ目に見える形で提示していくことにある。

それ故RUPは言わば小刻みなPDCAサイクルの絶え間ない反復を指し示している。方向付けフェーズでは既に「企画」の水準でシミュレートされた<結果>が要求され、遂行フェーズではアーキテクチャ・ドライバの特定に留まらずユースケース・モデルやプロトタイプによる<結果>の視覚化が求められる。制作フェーズは「ベータ版」開発として位置付けられ、移行フェーズすら「過渡期」と見做される。RUPはこの動的なサイクルの反復によって矢継ぎ早に<結果>を出し続けることで「速度」の問題を解消していく。

しかしRUPはそれ自体で完結した「マニュアル」ではない。これは「ライブラリ」のようなもので、各組織システムの開発プロジェクトに導入されて初めて成り立つ。RUPは、導入先の組織の企業文化やプロジェクトのユースケースに応じて、その都度「設計」されなければならない。

これを前提とすれば、オブジェクト指向設計思想は、RUPを運用する場合にせよ、RUPを実践する場合にせよ、重要な位置付けにある。RUPオブジェクト指向設計を推奨している。しかしそれだけではなく、RUPを運用する上ではオブジェクト指向設計思想に基づいた分析が必要になる。

問題解決策:概念水準と仕様水準と実装水準の区別

オブジェクト指向を理解するには、前段階として、「オブジェクト指向分析(Object-oriented analysis)」で言及すべき「概念水準の観点(conceptual perspectives)」、「オブジェクト指向設計(Object-oriented design)」で言及すべき「仕様水準の観点(specification perspectives)」、「オブジェクト指向プログラミング(Object-oriented programming)」で言及すべき「実装水準の観点(implementation perspectives)」の差異を理解しなければならない。この差異は、二重の区別によって成り立っている。つまりオブジェクト指向分析オブジェクト指向設計オブジェクト指向プログラミング区別と、概念水準(conceptual level)と仕様水準(specification level)と実装水準(implementation level)の区別だ。

責任としてのオブジェクト

オブジェクト指向におけるオブジェクトは単なる「データと操作の集合体」ではない。この前提に立ってしまうと、上述した三つの区別を理解することはできない。むしろオブジェクトは「責任」を意味する。インスタンスは責務を遂行する実体に他ならない。

概念水準のオブジェクト指向分析

RUPのようにオブジェクト指向に準拠した開発プロセスでは、まず全てのソフトウェアを網羅的に設計するのではなく、アーキテクチャ・ドライバに関わる基本的な仕様となるサブセットを設計していく。そのためには、まず開発しようとしているオブジェクトが「そもそも何なのか(What)」を特定しなければならない。初めからそのオブジェクトの「実装は如何にして可能になるのか(How)」を考えてしまっては、何を造れば良いのかも定まらない状況で開発を進めることになるため、不確実性が高まってしまう。

この「そもそも何なのか(What)」、すなわち「何の責任を担うのか」を特定する営みを概念水準での分析と呼ぶ。この分析の具体名がオブジェクト指向分析であると考えて良い。

実装水準のオブジェクト指向プログラミング

一方、やはり最終的には「実装は如何にして可能になるのか(How)」も決定しなければならない。この営みが実装水準となる。そして、この実装方法オブジェクト指向プログラミングということになる。

しかし、一つの開発で要求される責任が単一とは限らない。複数の責任問題となる場合、複数のオブジェクトが必要になる。場合によっては、オブジェクト同士を結合することで、複合的な責務を担保しなければならなくなる。

仕様水準のオブジェクト指向設計

そこでオブジェクト指向では特に、複合的な責任を果たそうとしている複数のオブジェクト結合部分となる公開インターフェイス仕様化が目指されることになる。ここでいう公開インターフェイス仕様化とは、あるオブジェクトがそのオブジェクトを「如何にして利用できるのか(How)」を共有する営みを意味する。

いわゆる「仕様」とは、インターフェイスのHow to use it.に他ならない。使い方がわからなければ、仕様書など無いも同然だ。そして、この仕様を共有する営みこそがオブジェクト指向設計に他ならない。GoFのデザイン・パターンは、まさに共通言語化による共有を目指した産物だった。

概念水準の分析結果と仕様水準のインターフェイスを介したコミュニケーションのメリット

概念水準仕様水準で要求や仕様の共有を行なえば、システムの概要を伝達することができる。実装水準コードの詳細を知らなくてもコミュニケートすることが可能になる。例えばRUPのソフトウェア要求定義やアーキテクチャ設計は、まずこの概念水準仕様水準でのみシステムを分析して設計していく。

この区別を導入していると、概念や仕様を変更せずに実装を変更することができるため、コードが改修されたとしても、その実装の詳細を知らない者でも、インターフェイスを介してそのオブジェクトを利用し続けることができる。これは、インターフェイスのユーザーを実装の変更による影響から守ることを意味する。

REST(ful)の仕様は、この概念と仕様の水準でコミュニケートしたわかり易い事例になっている。各APIのユーザーは、連携先のシステム内部の具体的な実装内容を全く知らない状態であっても、仕様として規定したHTTP(S)リクエストとレスポンスの形式に従うだけで、そのAPIを運用することができる。

機能テストと結合テストと単体テストの差異

オブジェクト指向を以上のように区別すると、テスト概念も適切に整理することができるようになる。「機能テスト(Functional test; System test)」と「結合テスト(Integration test; Interface test)」と「単体テスト(Unit test)」の差異は、責任という概念を前提として、概念水準仕様水準実装水準差異に対応している。

次のように纏めても良い。

以下、もう少し詳述する。

機能テストの前提

機能テスト概念水準の検証になる。「そもそも何なのか(What)」、すなわち「何の責任を担うのか」に関する当初の予定と、実際に実装されたコードをはじめとした成果物との間に矛盾が無いことを確認していく。

RUPの場合は特に、ソフトウェア要求定義などで取り上げたユースケース図やユースケース記述に成果物たるオブジェクトが対応し切れるか否かを検証する。いわゆる「ユースケース・テスト」は、ユースケース・モデルとコードの同期関係を確認するテストに他ならない。オブジェクト指向分析で組み立てたモデルと実際のコードが同期していなければ、そもそもその成果物は何の責任も果たしていないことになる。無価値と見做されても致し方ない。

「ユースケース・テスト」に象徴されるように、一連の機能テストでは、システムの内部を事実上考慮しない。丁度、ユースケース図がアーキテクチャの内部を事実上考慮しない理屈と同じだ。その意味機能テストは「ブラックボックステスト」であり、「限界値分析」や「同値分割」は「ユースケース・テスト」の機能的等価物として採用されても構わない。

結合テストの前提

結合テスト仕様水準の検証になる。仕様によって共有されるのは、そのオブジェクトが「如何にして利用できるのか(How)」だ。したがって結合テストで検証すべきなのは、そのオブジェクトインターフェイスが本当に仕様で規定されている通りに利用できるか否かになる。

言い換えれば、結合テストでは、インターフェイス同士の結合を検証する。これにより、各オブジェクトインターフェイスを介した相互利用が「仕様通り」に実現することを確認する。オブジェクト指向設計で組み立てたインターフェイスに関するモデルと実際のコードが同期していなければ、使用で説明しているHow to use it.に矛盾があるということになる。

その影響は他のオブジェクトに波及している。もし矛盾が見受けられるのならば、そのインターフェイスを抱えているオブジェクトの改修が必要になる。これは一種の「手戻り」であり、別途で退行テストが必要になる。ここでいう退行テストは、概ね単体テストのやり直しと考えて良い。

結合テストケースを考察する上では、多くの場合「粒度」が問題になる。同じ結合部分でも、「APIとAPIのマッシュアップ」と「PHPとMySQLの結合」とでは、粒度に差異が生じる。しかしこの問題に対する普遍的な正解は無い。どの粒度が適切なのかは、専らオブジェクト指向設計で選択する必要がある。

単体テストの前提

単体テスト実装水準の検証になる。つまり、「如何にして実装されるのか(How)」が問われる。詳細クラス図やシーケンス図やコミュニケーション図は実装を手引きしてくれるモデルとしても機能する。

しかしながら、実際のコードがこれらのモデルと同期していなければ、仕様を満たせないオブジェクトとして実装されている危険がある。また、モデルとコードが同期していないということは、後続の開発でそのモデルを見直しても、現状のコードを俯瞰的に観察することが不可能になる。これは、ソフトウェアの全体像を把握することが不可能であるということになるため、後続の開発者との仕様水準でのコミュニケーションが事実上不可能になっていることを意味する。

一般的に単体テストは致命的なバグを取り除くデバッグ作業と見做されている。いわゆる「ホワイトボックステスト」や「デシジョンテーブル」では、「分岐網羅」や「命令網羅」や「条件と動作の差異」などのような概念によって網羅性が問われている。

しかし、こうした網羅性の問題は二次的な問題として格下げしてしまって構わない。仮にあらゆる条件や可能性を考慮したテストを実施したところで、最終的に提出される成果物たるコードがモデルと同期していないようでは、利用価値が無い。後続の開発による保守や拡張において、モデルによる俯瞰的な全体像の観察ができないようでは、「再利用」の対象にはなり得ない。

むしろ単体テストの時点で消化しておくべきなのは、「クラス構造テスト」などのような静的テストだろう。個々のオブジェクトにバグが無い場合であっても、クラス間の継承、多重度、凝集度、可視性などによって、思わぬ修正や機能追加を余儀無くされる場合もある。

問題解決策:RUPと既存の開発プロジェクトの「共通性/可変性分析」

以上のオブジェクト指向設計思想を前提とすれば、RUPを開発プロジェクトに導入する場合にも、オブジェクト指向で考えていかなければならないことがわかる。と言うのも、RUP拡張可能性と再利用可能性を確保したパッケージとして提唱されているからだ。この意味で言えば、RUPの運用や実践は等価機能主義的になる。言い換えれば、RUPと既存の開発プロジェクトの関係は相互にブラックボックス化されている必要がある。

ジム・コプリエンが提唱したオブジェクト指向分析の一つである「共通性/可変性分析(commonality/variability analysis)」は、ソフトウェアが相対する問題の領域を設定する上での問題解決策であった。しかしこの分析方法は、RUPと既存の開発プロジェクトの関連においても再利用されるべきであろう。

共通性分析と可変性分析の差異

共通性/可変性分析は文字通り「共通性」と「可変性」の区別を導入する分析だ。ただしこの区別は、概念水準仕様水準実装水準区別と重なり合っている。

共通性分析は、カプセルの中で纏め上げることで共通化することが可能な流動的諸要素の属性を意味する。一方で可変性分析は、共通化した場合に、特定のユースケースで逸脱し得る流動的諸要素の属性を意味する。

共通性分析の観点は、概念水準仕様水準に向けられる。可変性分析の観点は、仕様水準実装水準に向けられる。共通性分析における仕様水準の観点によって、「概念水準から観た場合に共通して立ち現れる諸概念」を網羅して参照できる「インタフェースの仕様」を洗い出すことが可能になる。一方、こうしてある仕様水準共通性が設定されることで、その共通部分との「差異」を伴わせる特化型のユースケースに対応した実装水準方法を決定付けることが可能になる。

共通性の観点から、言わば共通する諸概念を纏め上げた抽象クラスが抽出される。一方これに対して可変性の観点からは、当該抽象クラスの共通処理部分との「差異」に対応付けるように、特化した処理を記述すべき具象クラスが抽出される。

自己論理的なオブジェクト指向のシステム合理性

RUPの開発プロセスを既存の開発プロジェクトに導入する場合には、既存の開発プロジェクトとの共通性可変性区別できるようにしておいた方が無難だ。既存の開発プロジェクトと共通している部分については、そのまま抽象化して関連付ければ良いだろう。だが可変となる部分に関しては、RUPを導入した場合の個別具体的なプロセスを明確化しなければならない。

例えば関数型プログラミングが根付いている開発プロジェクトに対してRUPを導入する場合には、関数型プログラミングとオブジェクト指向プログラミングの「差異」を意識せざるを得なくなるはずだ。既存の開発プロジェクトには、既に関数型プログラミングを前提とした設計思想が普及しているかもしれない。可変のメンバ変数に対する参照すら許さないプロジェクトのアサインメンバーに対してオブジェクト指向へのパラダイム転換を要求して従事させるのは至難の業だ。

尤も、この関数型プログラミングとオブジェクト指向プログラミング差異に付随する問題に限って言えば、典型的には概念水準仕様水準実装水準区別の導入をそれこそオブジェクト指向分析的に実践することによって解消できるだろう。何故なら、関数型プログラミングとオブジェクト指向プログラミング差異は、精々のところ実装水準の全般と仕様水準の一部の問題でしかないからだ。たとえRUPの開発プロセスに従わない関数型プログラミングの担い手が実装水準を担当していたとしても、その成果物が仕様水準指し示した公開インターフェイス仕様を満たしてさえいれば、その関数型プログラミングが有害になることはあり得ない。

無論、このことの前提にあるのは概念水準仕様水準アーキテクチャ設計が徹底されているということだ。とりわけアーキテクチャ中心設計によって抽出されたアーキテクチャ・ドライバは、オブジェクト指向的には概念水準仕様水準にしか対応しない。実装水準に対応するのは、アーキテクチャ設計とは区別される詳細設計だ。

つまり、アーキテクチャ・ドライバの抽出をはじめとしたアーキテクチャ中心設計オブジェクト指向分析オブジェクト指向設計によって実践するソフトウェア・エンジニアやアーキテクトは、あくまでも仕様水準を介した詳細設計とのブリッジを心掛けることが求められる。そして、その仕様水準指し示しインターフェイス仕様が守られている限りにおいて、アーキテクチャ設計者は詳細設計者に自由度を提供しなければならない。言い換えれば、オブジェクト指向設計を実践するアーキテクチャ設計の担い手は、仕様水準における「非オブジェクト指向設計的な詳細設計」や、実装水準における「非オブジェクト指向プログラミング」の担い手たちさえも許容しなければならない。

これこそが、オブジェクト指向を自己論理的に適用した場合に到達する結論だ。オブジェクト指向的なアーキテクチャ設計を担当するソフトウェア・エンジニアやアーキテクトは、言わば仕様水準詳細設計実装水準のプログラミングという活動そのものをブラックボックス化して観察しなければならない。我々は、オブジェクト指向を徹底すればするほど、非オブジェクト指向に対してすら寛容にならなければならないのである。もはやアーキテクチャ設計を敢行する我々にとって、例えばアラン・ケイのメッセージに耳を傾けることも、ビヨルネ・ストロヴストルップと共に『オブジェクト指向とは何か?』を論じることも、二次的な問題でしかなくなる。オブジェクト指向と非オブジェクト指向区別をこの区別そのものに再導入(re-entry)することにより、非オブジェクト指向をもオブジェクト指向的に参照することを以って、アーキテクチャ設計システム合理性を確保していく必要がある。

スポンサーリンク