用語の解説は一応したつもりなので、そろそろ本題に入りたいと思います。
挑戦してみたいのは、オブジェクト指向を定義からトップダウン式に展開していって、
オブジェクト指向プログラミングで使われている技術(カプセル化や継承等)につなげてみたい、ということです。
今回大量に未証明のものがありますが、それはおいおい埋めていければいいと思ってます。
若干弱いところはあるけれど、矛盾とまではいわない程度になっていると思います。
私の考えるオブジェクト指向理論の定義は次のものです。
@1.「リソース」を「オブジェクト」として「捉える」。オブジェクト指向XXでは、「オブジェクト」のみを扱う
とっちゃんさんにまとめてもらったものを利用するとこうです
「AをBの中に概念化したA'を重視する」
Aをリソースと呼び、Bを主体と呼び、A'をオブジェクトと呼ぶことにします
A、Bにはどんなものを当てはめても成り立つとします。
(B=主体を、開発者固定にしている人は要注意です。がんがん切り替えていきます)
@1を展開していきます。
#1.捉えるということは、それを行う主体がいる
#2.捉えるというのは、主体の中にリソースをオブジェクトとして再構築すること
#3.オブジェクトは主体の中にあるので、主体がオブジェクト制御の全権を握る
#4.リソースは主体の中にあるとは限らないので、主体はリソースに対する制御権をもたない
#5.捉えるというのは正当性の基準をもつということ。
#6.オブジェクトに、そのオブジェクト及び関連付けられたリソースの正当性を保証する義務がある。
#7.オブジェクトは、主体になることができる
上記7点が全て正しいなら、ちょっとした定理が導けます。
それは、(主に#4により)オブジェクトは常に有効でいることはできないので、
全てのオブジェクトは無効になることをサポートしなければいけない、ということです。
無効になることをサポートするというのはつまり、オブジェクトが有効かどうかを判定するメンバ関数を作る、というのと同じです。
つまり全てのクラスの基底となるObjectクラスにオブジェクト-リソース間の正当性チェック関数を持たせるべき、となります。
オブジェクト指向の定義から導かれる、Objectクラスに必要なものは、
ToString()でもなく、Serialize()でもなく、リソースまで含めた正当性判定ではないかという提案です。
(ToString()で代用できなくはないですけど)
class Object{
//この関数が呼ばれた時点で、このクラスが正しいといえるかどうかの判定関数
//クラスが正しくないとはどういうことか↓
//この関数は、この関数(とOpenXX)以外の全てのメンバ関数の先頭で呼ばれ、失敗(!=0)時はそのメンバ関数が失敗する..ことになっている
//Isで始まらないいい名前がなかったので、SelfTest()=自己診断としました
virtual int SelfTest()=0;
};
似たような概念に、IsInvalid() があります。
ただこれは、クラス-クラスインスタンス間の正当性チェック(つまりnewした結果が失敗かどうか)を調べるというニュアンスが強いのと、(私の採用する規約だと)Isで始まる関数はboolしか返せないので、新しく名前をつけました。
重要なのは、型のチェックだけではなく、意味のチェックもするということです(意味、概念という外部のリソースとつながっているはずなので。)。
だからこの関数が調べる必要があるのは、
・外部から提示された要求仕様
・型として正しいかどうか
・メンバ変数(クラスを使う人という視点から見ればリソースにあたる)
・グローバル変数(普通はないと思いますが)
・環境変数(OSの設定、関連ファイルなど)
となります。
無効をサポートしない手法だと案外大変そうな、子供クラスで実装してみました。
class Child : public Object{
int m_age;//メンバ変数はクラスを使う人から見るとリソース。このクラスを使う人からは直接制御権がない
public:
Child() : m_age(-1){}
//クラスは、自分が正しい状態にあるかどうかを判定できなければいけない
//Child が、クラスとして有効であるために少なくても満たさなければいけない条件判定//制約条件
int SelfTest(){
if(m_age < 0){return 1;}//生まれてなければ、Childとして認めない
if(m_age > 12){return 2;}//12歳より上なら、Childとして認めない
return 0;
}
};
class Child : public Object{
Human m_human;//メンバ変数はクラスを使う人から見るとリソース。このクラスを使う人からは直接制御権がない
public:
Child(){}
//クラスは、自分が正しい状態にあるかどうかを判定できなければいけない
//Child が、クラスとして有効であるために少なくても満たさなければいけない条件判定//制約条件
int SelfTest(){
if(int res = m_human.SelfTest()){return res;}//人として正しくなければ子供として正しくない
// if(m_human.GetAge() < 0){return 1;}//生まれてなければ、Childとして認めない//←このチェックはHumanに移管
if(m_human.GetAge() > 12){return 2;}//12歳より上なら、Childとして認めない
return 0;
}
};
@1.「リソース」を「オブジェクト」として「捉える」。オブジェクト指向XXでは、「オブジェクト」のみを扱う
ので、SelfTest()関数が必要
これがきれいにつながるかどうかが前半の山場です。
これが成り立てば、全てのオブジェクトは制約オブジェクトとして扱えるようになります。
そうなれば、それを使って継承等の仕組みを説明できる・・ような気がしますよね?