2015年6月14日日曜日

オブジェクト指向からコンポーネント思考へ

「オブジェクト指向」と検索してざざっとでてくる用語。

「カプセル化」「多態性」「再利用」「リファクタリング」「プロトタイプベースオブジェクト指向」「クラスベースオブジェクト指向」「振る舞い」「UML」「抽象化」「デザインパターン」

上記のようないろんな用語以外にも良くわからない技術で表現していたりして不明瞭さたっぷりです。

つまりオブジェクト指向とは万人が同じ答えにならない、「不明瞭」な物を扱っている覚悟をしておく必要があります。

工業製品としてソフトウェアを作るなら誰もがある一定上の同じモノができるようにモノサシやテンプレートが必要になると思います。

そこで古くからあるコンポーネント思考の導入の始まりです。

ソフトウェアの「職人技、一品物」から「工業製品、量産品」のように扱える、そんなやり方を説明していきます。

~~~~~~~~~~~~~~~~~~

まずはおさらい、オブジェクト指向について

要点だけをまとめると、

・オブジェクト指向とは、オブジェクトを表現するものがクラスであること。
 クラスがオブジェクトを表現するんじゃなく、本質は逆なのです。

 例えば、目の前にある人物像のデッサンする場合、
 目の前にあるのがオブジェクトであって、キャンバスに描くのがクラスです。
 クラスを作ってから目の前のオブジェクトは作れないんです。
 「鳥取は島根の隣」と同じ次元では無いことがわかったでしょうか。
 補足するとデザインパターンにFactoryパターンというがあるのですが、命名した人は根本的な間違えに気づいてないと思います。
 


・インスタンスとは、元は図形をコピーするという意味。
 現在の言語仕様にあてはめるとクラス(設計図)のコピーにあたる。生成物ではない。
 オブジェクトは生成じゃなく、複製だからです。
 目の前にあったオブジェクト(人物像)をキャンバス(クラス)に描いて出来たものがオブジェクトなので、生成じゃなく複製が正しい表現となります。


・オブジェクト指向には多態性と振る舞いは不要。
 オブジェクト思考のメリットで騒がれてる事はすべて投げ捨ててしまいましょう。
 これらは後付の理由なので本質を知る上では不要です。

 そうすることでオブジェクトそのものをクラスに表現できるようになります。
 オブジェクトをクラスに表したものをModelクラスと呼んでいます。


----

class PenModel
{
//ペンに関する関心のあるプロパティを記述。
public int Length { get; set; }
public Color Color { get; set; }

//最初はメソッドは用意しない
//メソッド追加する場合は、オブジェクトの変化を与えるメソッドであって振る舞いでは無い。
//振る舞い=イベントなので動的、状態変化は静的、同じ用に見えて異なるからです。

//これはOK
public void ChangeColor(Color newColor)
{
this.Color = newColor;
}

//これはNG、後で説明するが、直感的な動詞はController向けの名称
public void Write(int count)
{
this.Length -= count;
}

//これはOK
public void DecLength(int count)
{
this.Length -= count;
}
}

----

class Main
{
public static void Main()
{
//求めているペンを生成する
var blackPen = new Pen { Length = 10, Color = Color.Black };
var redPen = new Pen { Length = 10, Color = Color.Red };
}
}

----

私はMVCフレームワークの影響を受けてしまっているのでModel部分のメソッド定義はController側に分離してしまっているのでそうなっています。
そうすることで、オブジェクトの本来の性質が表に出て分かりやすいと思うし、値の検証もしやすさと多態性の表現をControllerで表せれる点など思うことは沢山あります。
まあ独自の書き方ですが、

class Model
{
...(関心のあるプロパティと変化メソッド)
}

//多態性はコントローラクラスで分けて表現
//同じ動作する関数が増えていくのがキライって方はModelクラスをpartialしてインターフェイスで分けて
//内部情報の書き換えはPrivateにする事で解決します。
//(オブジェクト単体で見るとすべて丸見えだけど、インターフェイス経由なら制限できるので)
//ちなみにControllerクラスには操作するModel以外の内部変数は極力作らない事です。
class Controller1
{
Model model;
public Controller1(Model model)
{
this.model = model;
}

public void Write(string message)
{
Console.WriteLine(message);
model.DecLength(message.Length);
}
}

class Controller2
{
Model model;
public Controller2(Model model)
{
this.model = model;
}

public void ChangeColor(Color newColor)
{
this.model.ChangeColor(newColor);
}
}


雰囲気だけは伝わったかと思います。
あとは何回か書くと理解が進むんじゃないかな。
再利用についてはコンポーネント指向の回で説明します。

○オブジェクト指向の設計手法
 オブジェクトの表現について理解できたならば、
次はオブジェクト設計ですが、

間違えやすいポイントを最初に持ってくると、基本的な考え方は
対象となるオブジェクト同士の関係が

 1:1, 1:N, N:1, N:N

であるかがポイントになります。


例えて男女関係と見立てると複雑度が分かりやすいです。

1:1の関係はごく普通。

1:N、N:1の関係はおや?ってなりますね。ここは何とかしなくてはなりません。

    プログラム的にいえばModelクラスを複数束ねるCollectionクラスを用意するなどです。

N:Nはもはや分かりません。

つまりオブジェクト設計とは

1:1の関係に持っていく作業です。


設計の仕方は仕様の元となるクライアントとヒアリングして

ざっくりと全体図を書きます。

プログラムとは自動化するのが本来の目的なので

現状、人手でやることをすべて表すことと関係する役者をすべて並べること作業の事です。

これらをユースケース図を使って書きます。

全部書き表したらよく言われる「汎化」作業に入るのですが、ここで注意ポイント

 勝手に減らしたりくっつけたりしない事、です。

オブジェクト指向の説明でデッサンの例のように

聞いた内容=オブジェクト

なので、それをユースケース図で書き表すのが作業です。

勝手にアレンジを加えるのはNG、提案はOKですが仕様の構造を変えるような提案はNGです。

現状をそのまま書き表して確認する、これが重要であるからです。

なぜそこまでして現状そのままに書き表すのかというと

現実と1:1で合わせておくことで「実はこうだった」など後出しの変更に強いからです。

ユースケース図を描いてN:Nの箇所があれば、ヒアリングして別の担当者になる人を探したりする作業も

オブジェクト設計のうちの1つになります。

この時点でなるべく1:1になるようにするのが設計の最適化。

もしNのオブジェクトで違う作業があるなら再度ヒアリングして分割できるかチェック、

だめならばプログラム上、多態性で表現することになります(←これが後々の問題の種になる事を承知の上でね。)

もちろんユースケース図に間違いがあった場合、仕様バグなので論外です。

-------------

ここまでで
 オブジェクト指向とは、オブジェクト設計と実装方法について説明しました。


ここからコンポーネント指向についてを述べていきます。

おなじみとなったVB、C#の開発環境でデザイナー画面にアイテムを設置していくだけでプログラムが出来てしまう

あれがコンポーネント指向です。アイテムはコンポーネントと呼ばれているので気づいた方はいるんじゃないでしょうか。

あの機能はDelphiが元になっていて、MSが開発者を引き抜いたりお金の力で出来たのが今のVisualStudioとC#言語仕様になります。

http://ja.wikipedia.org/wiki/Visual_Component_Library

VLCの基本構造は最下層になるTObjectクラスがあって派生でトップに立ったものがコンポーネント、いわゆるソフトウェア部品・モジュールとなります。

底のオブジェクトがあって最上位になっていくほど高機能になっていく構造です。

車輪の再開発ですが、これをプログラムで表現しようという試みです。

全体ブロック図

+--------------------+----
| 業務画面                    |
+--------------------+  ビジネス領域(フォーム画面に部品ペタペタ、イベント広げて処理書いたり、、)
| ビジネスロジック          |
+---------+----------+----
|     Library |                  |  コンポーネント部品(.NETから派生するのでこの位置をキープ)
+---------+                 |
|       .NET                     |
+--------------------+----


今回着目する箇所はLibraryと書かれている部分です。

Libraryのブロック図

+--------------------+---
|Controller               |  共通操作
|                           |  Modelクラスには、Driverモデルと共通パラメータ、共通操作
+--------------------+---
|Driver(Model)           |  プロトコルレイヤー(外部と通信プロトコル(コマンド)で操作を行う)
|                           |  Modelクラスには、通信仕様上で必要になるパラメータ(IDなど)、コマンドメソッド
+--------------------+---
|外部IO(Model)              |  外部入出力レイヤー(シリアルポート、ファイル、TCP/IP 通信を行う)
|                                  |  Modelクラスには、ポート初期化パラメータ、Open/Close/Read/Write
+--------------------+---


こんな具合でしょうか。

大体は外部と通信するのでオブジェクトの体は通信インターフェイスにします。

その上に相手とのコミュニケーションする電文の作成・解析が必要になってきて

さらに上になると判断してコミュニケーションする頭脳という具合ですかね。

IOが足回り、Driverが手足、Controllerが頭脳、こうなります。


例でいうとTCP/IPで相手と通信する場合、通信仕様が必要になるので、先ほどのブロック図に当てはめていけばコンポーネントの完成。

つまり再利用を重視した構造であるということになります。

使うユーザーは最初に書いたとおり、VisualStudioを操作して画面に部品を設置するだけでプロパティウィンドウに書かれたプロパティを設定していくと
通信仕様とは一切知らなくても通信プログラムが完成します。

こういった部品を沢山作って行くことで

画面と状態遷移とビジネスロジックをプログラムにする作業に集中できるようになります。


大規模になってもオブジェクト指向で設計をがんばってもらって

足りないコンポーネントは作って、

ビジネスロジックと1:1で向き合える大きさまで派生させて、

画面単位にコンポーネントをくっつけて、

イベント広げて処理を書いてビジネスロジックを動かす、

画面遷移するなら遷移する、終了するなら終了で。

そんな作業の流れができてくればコーディング量も減って品質の向上につながるのではないでしょうか。

色々書いてきましたが、

オブジェクト指向は設計用で実装用ではないです。

設計と実装を1:1にするのもありですが、上記作業なら無理して合わせる必要もないですね。

ここでの注意ポイントはビジネスロジックにパラメータは持たない事です。


以上、思考の整理とともに長々と書いてみました。

0 件のコメント:

Androider