関数言語風なところ(LINQ)や、手続き型の関数の羅列を取り除いて、オブジェクト指向オンリーでプログラミングをする言語を妄想(笑)してみます。個人的には、純粋オブジェクト指向言語は Java がそれに近いのですが、もうちょっと何とかするいう形で。
まず、オブジェクト指向言語の重要な要素を挙げると、
1)カプセル化(隠蔽化)できる。
2)ポリフォリズム(多態化)できる。
3)継承関係がある。
なところです。
ここで、焦点を当てるのは「カプセル化」です。
もともと、オブジェクト指向の最初は、オブジェクト同士のやりとは「メッセージ」を使ってやり取りをしています。
1 | objectA -> message -> objectB |
のような繋がりになります。このメッセージ(message)は、objectA のメソッドなのか、objectB のメソッド(リスナー?)なのかは、定義されません(とか思っています)。
また、メソッド(関数)の呼び出しは、C言語やC++の表記上、複数の引数とひとつの戻り値、という決まりがありますが、実は戻り値は複数でも構わないのです(F# のタプルと同じですね)。
1 2 3 4 | objectA.Method { (retA, retB, ... ) = objectB.Method(paramA, paramB, ... ) } |
な形です。
さて、手続き型の流れの場合は、if 文を使ってフロー制御をしますが、純粋オブジェクト指向の場合は if 文の扱いは、ちょっと慎重になります。
1 2 3 4 5 | if ( a != null ) { if ( b != null ) { objB.Method( a, b ); } } |
のように呼び出し前にチェックを入れますが、純粋オブジェクト指向では呼び出し時のチェックをしません。
これは、パラメータの整合性のチェックは、呼び出されたオブジェクト側の責務であり、呼び出し側では判断がつかないためです。もう少し詳しく言うと、隠蔽化を進めるとどのパラメータが正常値であるのかは、呼び出されたオブジェクトしか分かりません。
なので、責務を明確にするために、
1 2 3 4 5 6 7 8 9 | // 呼び出し側では、パラメータチェックをしない objB.Methd( a, b ) // objB の中でパラメータチェックをする void objB.Method( string a, string b ) { if ( a == null ) return ; if ( b == null ) return ; // メソッド内の処理をする } |
この場合、objA から objB への問い合わせが非常に多く発生するため、パフォーマンス的には良くない(と想像される)のですが、オブジェクト指向の視点から言えば、これが正しいのです。なお、オブジェクト指向でのメソッド呼び出しのコストは「0」となっています。なので、呼び出しが多くても少なくてもスピードは関係ありません(ということになっています)。
この部分、責務を明確にする、ということで、objA で判別するべきか、objB で判別するべきか、というので見解が分かれそうですよね。例えば、次のコードを見てください。
1 2 3 4 5 6 7 8 9 | if ( a == "masuda") { objB.Method( a, b ); } // objB の中でパラメータチェックをする void objB.Method( string a, string b ) { if ( a == null ) return ; if ( b == null ) return ; // メソッド内の処理をする } |
こうなっていた場合、a == “masuda” は何を意味するのでしょうか?ここだけ見ると、変数 a の内容が “masuda” の時に、objB.Method を呼び出す、という条件にしかならないので、これがオブジェクトの内部を考慮してのものなのか、オブジェクトを呼び出すときの条件なのかが判断がつきません。なので、他のところで、
1 2 3 | if ( a == "tomoaki" ) { objB.Method( a, b ); } |
と違う値で呼び出していることを確認した時に、オブジェクトの呼び出し側がつけている条件(呼び出されているクラスの前提条件ではない)ということが判断できます。
なので、書き方として、
・呼び出し側で判断する制御文
・隠蔽化を強化するための制御文
の2つを区別しないといけません。
1 2 3 4 5 6 7 8 9 10 11 | objB.Method( string a, string b ) { // 前提条件 if ( a != null ) { if ( b != null ) { // 処理内容 if ( a == "masuda" ) { objC.Method( a ); } }} return ; } |
のように書き分ける必要があるのですね。
たしか、アスペクト指向の書き方として、そんなのがあった覚えがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | obj.Method.Before( string a, string b ) { if ( a == null ) return ; if ( b == null ) return ; } obj.Method( string a, string b ) { // 処理内容 if ( a == "masuda" ) { objC.Method(a); } return ; } obj.Method.After( string a, string b ) { ... } |
Method.Before では、本体 method の前処理を担当して、主にパラメータをチェックします。
Method.After では、後処理を担当します。
…と書いたところで、力尽きました orz もうちょっと大雑把に議論したほうがいいみたい。