ちょっとリハビリテーションがてら徒然に。
結論から言うと、オブジェクト指向設計を導入したからといって、プロジェクトが成功するとは限らず、逆にプロジェクトが失敗したからといって、オブジェクト指向設計を導入しなかったから、というには言えません。当たり前ですが…因果関係はそれほど強くない。
「○○設計」なり「○○手法」というものがあって、それを導入したからと言って必ず成功するとは限らない。言えるのは、成功する確率が上がるということしか言えない。逆パターンもあれば、導入すると失敗するとは言えないけれども、失敗する確率が上がる(成功する確率が下がる)パターンもある。これは、何を言うのかというと、「○○手法」を導入する場面、場所(人員も含む)、規模など諸々の条件があって、それが有効に働く場合と、働かない場合、むしろマイナスに働いてしまう場合もある、ということ。
が、かといって「○○設計」なり「○○手法」なりを導入しなくても良いかと云えば、そうでもなく、それは導入するということに対して、現時点のプロジェクトの成功だけはなく、次のプロジェクトの成功確率を上げるなり、他社との競争力を上げるなり、個人的なプログラミング能力をあげるなり、その場の流行であったり(これはモチベーションに関わる問題であたり)するわけで、単純にプロジェクトの成功/失敗だけによる結果だけで見るのは早計であるのです。
さて、長い前置きはそれとして、現実問題として「オブジェクト指向」を導入したほうがよいのか、あるいはオブジェクト指向的なアプローチを利用したほうがよいのか、という問題に話を始めると、
- 「オブジェクト指向」を導入するならば、良いアプローチをせよ
ということに尽きる。逆に言えば、
- 「良いアプローチ」ができそうでなければ、そのプロジェクトに「オブジェクト指向」を導入すると失敗する確率が高くなる
ということになる。
プロジェクトマネージメント的な言い方をすると、大きなプラスを求めるのではななく、大きなマイナス(あるいは小さなマイナス)を早期に発見・対処することによって、マイナス分を減らして、全体としてプラスに傾くようにするやり方が妥当である、ということになる。
具体的に云えば、
- 関数的なアプローチしかできない人員の場合には、「オブジェクト指向」アプローチを避ける。
- データ指向優先で、システム構成が決まっている場合は、「オブジェクト指向」を避ける。
- システム設計ができていない場合は、「オブジェクト指向」を避ける。
- 共通化部分が極端に少ない場合は、「オブジェクト指向」を避ける。
という判断があって、この「避けるべき条件」が積み重なるイコール、先行きプロジェクトが危うくなる条件がある、勝ってしまう場合には、オブジェクト指向を避けて、もともとある手続き型なアプローチや、データ指向型の設計を優先させる。
ちなみに、.NET で Windows フォームを使ってアプリケーションを作る場合、最低限 UI 部分はオブジェクト指向的、かつクラスライブラリを使う部分はオブジェクト指向的になるのだが、実際にプログラマがコーディングしなければいけない手続き部分は、さほどオブジェクト指向的にはならず、VB6 の頃からある手続き型のコーディング(関数型のコーディング)になる。
逆に「良いアプローチ」がどのようなポイントを示すのかというと、
- システム設計段階で、オブジェクト指向的なアプローチがある。
- NUnit などの自動化のテストケースを積極的に利用する用意がある。
- オブジェクト指向のコード粒度や複雑度の話が理解でき、実行できる。
というものがある。
要件定義からシステム設計に落とすとき(システム設計というのは、ユーザーの要件をどのようにコンピューターが実行するのか、という物理設計を示している)、ここで手続きの流れのまま設計から実装まで突き進んでしまうのか、一度、共通的な手順、今後の拡張、クラスの強度などを考え直すうえで、システム構造をクラスとして考え直す、という流ればあれば、オブジェクト指向的なアプローチを導入する価値はある。
このあたり、要件定義→設計という流れの中で、これがウォーターフォール的に進むのか、イテレーション的に進むのか、アジャイル的に進むのか、というプロジェクトの進行の問題もある。数年前、このあたりを「アーキテクト」という言い方をしていたような気もするのだが、それはバズワードだったのか?、はここでは問わない。
ひとつ言えるのは、完璧な設計は存在しないし、完璧にオブジェクト指向を進めることは不可能であるので(完璧に近づくことが可能だにしても)、何らかの形で「後戻り」なり「再設計」が必要になる。ならば、現実的に考えて、「再設計あり」でプロジェクトを進めるのも大切であろうということ。逆に、再設計されそうな部分を「予知」しておくことも大切ということ。
このあたりは、職人的な勘というか、動物的な勘というか、そういう匂いがある場所を監視しておく(常に気にしておく)こととなる。体系的に学ぶことも可能だと思うのだが…それは別の機会にでも。
NUnit などの自動化のテストケースを導入する場合、手続き型の関数アプローチでも可能ではあるのだが、ものが大きくなれば、オブジェクト指向的なアプローチでクラスを分けたほうがやりやすくなる。と言うのも、自動化をする場合、対象のメソッドを動かすための前提条件を設定することになる。この前提条件が多ければ多いほど、テストケースを作るのは大変になる。なので、どのデータを設定したあとに、どのメソッドを動かすのか、という範囲が狭まればテストケースを作るのが楽になり、結果的にコーディングをしている時間が短くなり、プロジェクトに時間の余裕が出てくる。ゆえに、プロジェクトの成功確率は高くなる。
逆に言えば、オブジェクト指向、特に隠蔽性なり疎結合の話が理解できていないと、クラス構造に分けたとしてもクラス同士の連携が多くなりすぎて、自動化のテストケースのメリットは薄れてしまう。テストケースが、テストデータに密接に繋がってしまう故に、使い捨てのデータ(テストデータのためのデータ準備)が大変になってしまう。
で、まぁ、今回の業務的なアプローチ(プログラミングを行うという意味で)としては、大きな失敗を避けることはできたものの、いまいち「効果」が実感できるところまでいかなかった、というのが不満ではあります。この手の新規アプローチの導入に関しては、「失敗を避ける」というのが第一であって、実は「成功させる」というのは二の次なんですね。導入の説明としては「成功」へ導くわけですが、実はプロジェクトが失敗してしまうと、二度とそのアプローチに手がでなくなるという大きなマイナスがあるので、なんらかの形で「失敗させない」ことが優先事項になります。
なので、結果的に言えば、成功する失敗するの要因は、オブジェクト指向の導入や、NUnit の導入によるものではなくて、「失敗しない要因を揃えていく」あるいは「失敗しそうなアプローチを避ける(避けさせる)」というのが、今回の仕事だったりして。