JavaScriptでオブジェクト指向しよう3・継承(1)


     さて、以上のようにしてクラスを作ることが出来ました。しかしこれでぱーふぇくとなオブジェクト指向プログラミングであるかといったらそうではありません。
     クラスに、重要な機能である「継承」をサポートさせてこそ真のオブジェクト指向であるといえるでしょう。
     継承。これは親クラスの性質を受け継いだ子クラスを作る機能です
     この機能はこんな場合に使います。
     この例題ではchara(キャラ)というクラスを作りました。このキャラに、もっとプロパティを持たせたいと思いました。しかしキャラの中には魔術士戦士もいます。魔術士には魔力プロパティを持たせたいし、戦士にはプロパティが必要です。逆に魔術士に力はいりませんし、戦士に魔力はいりません。なので、charaクラスに力と魔力、二つのプロパティを作ることは出来ません。
     どーしましょう?
     この問題を解決するために、wizard(魔術士)クラスとsoldier(戦士)クラスを新しく作ろうという方法が考え出されます。となると、この二つのクラスがそれぞれ持つプロパティとメソッドはこうなります。

    wizard(魔術士)クラス
    name (名前)
    age (年齢)
    sex_id (性別ID)
    sex() (性別関数)
    mp (魔力)
    soldier(戦士)クラス
    name (名前)
    age (年齢)
    sex_id (性別ID)
    sex() (性別関数)
    pw (力)

     しかし。この二つのクラス、よく見ると一番下の魔力、力以外は全部一緒じゃないですか。一緒なのに別々に作るのは面倒ですね。一つにまとめちゃえるところはまとめちゃうのが美しいプログラムであると一般的には思われています。
     という事で、魔力、力以外のメンバは元々のcharaクラスに押し込めたまま、新しく、魔力だけを持ったwizardクラス、力だけを持ったsoldierクラスを作り、この二つのクラスから、charaクラスのメンバ(プロパティ+メソッド)をあたかも自分のクラスのメンバであるかのように使ってしまおう、ということをやります。前置きが長くなりましたが、これが継承というものです。

    chara(キャラ)クラス
    name (名前)
    age (年齢)
    sex_id (性別ID)
    sex() (性別関数)
    wizard(魔術士)クラス
    charaクラスの全部
    mp (魔力)
    soldier(戦士)クラス
    charaクラスの全部
    pw (力)

     charaクラスが親クラスで、wizard、soldierクラスが子クラスという風になります。charaクラスの性質を、wizard、soldierクラスは全部受け継ぐ事ができます。魔術士も戦士も「キャラ」であることには間違いないわけですからね。
     しかしここで問題が発生します。
     JavaやC++では、一言、この子はこのクラスの子クラスですよ〜と言ってあげればさくっと継承が出来てしますのですが、悲しいかなJavaScriptには継承がサポートされていません(*最近のJavaScript1.3にはcall()という関数があるんですが、新しすぎて今現在、うちのブラウザIE5ではサポートされていません)。
     従って継承のしくみを自分で作ってあげなければなりません。
     継承。親から子へ受け継がせる。……つまりは、親のメンバを全部子供にコピーしてあげればよいわけですね。
     という事でそのまんま、親のメンバを一つずつ子にコピーするという関数、extnds()を作ってみました。赤字の関数です。

    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function main(){
    	no=new Array();		//オブジェクトの配列
    	cnt=0;			//カウンタ
    
    	no[0]=new wizard("Will", 18, 1, 90);		//ウィル 18歳 男 魔力90
    	no[1]=new soldier("Sophia", 16, 2, 100);	//ソフィア 16歳 女 力100
    	no[2]=new soldier("Dilt", 19, 1, 50);	//ディルト様 19歳 男 力50
    	no[3]=new wizard("Cailtag", 23, 1, 120);	//カイル 23歳 男 魔力120
    	no[4]=new wizard("Lute", 23, 1, 70);		//リュート 23歳 男 魔力70
    
    	for(cnt in no){		//配列noいっぱいまでループ
    		document.write(no[cnt].stts(no[cnt]));
    	}
    }
    
    //*********** charaクラス *************************************
    function chara(name, age, sex_id){ 	//コンストラクタ(もどき)
    	this.name=name;			//名前
    	this.age=age;			//年齢
    	this.sex_id=sex_id;		//性別ID
    	this.sex=chara_sex;		//性別
    
    ////// メンバ関数(メソッド) sex()
    ////// 性別IDから該当する性別(文字列)を返す
    	function chara_sex(id){
    		str="";
    		switch(id){
    		case 1:	str="Male";		//性別ID=1 男性
    			break;
    		case 2:	str="Female";		//性別ID=2 女性
    			break;
    		}
    		return str;
    	}
    }
    
    //*********** wizardクラス *************************************
    function wizard(name, age, sex_id, mp){	//子クラスのコンストラクタ(もどき)
    	c=new chara(name, age, sex_id);
    	extnds(this, c);
    
    	this.mp=mp;			//魔力
    	this.stts=wizard_stts;		//ステータス表示
    
    ////// メンバ関数(メソッド) stts()
    ////// 名前、年齢、性別、魔力を文字列で返す
    	function wizard_stts(obj){
    		str="Name: "+ obj.name +" <BR>";
    		str=str + "Age: "+ obj.age +" <BR>";
    		str=str + "Sex: "+ obj.sex(obj.sex_id) +" <BR>";
    		str=str + "MagicPower: "+ obj.mp;
    		str=str + "<BR><BR>";
    		return str;
    	}
    }
    
    //*********** soldierクラス ************************************
    function soldier(name, age, sex_id, pw){//子クラスのコンストラクタ(もどき)
    	c=new chara(name, age, sex_id);
    	extnds(this, c);
    
    	this.pw=pw;			//力
    	this.stts=soldier_stts;		//ステータス表示
    
    ////// メンバ関数(メソッド) stts()
    ////// 名前、年齢、性別、力を文字列で返す
    	function soldier_stts(obj){
    		str="Name: "+ obj.name +" <BR>";
    		str=str + "Age: "+ obj.age +" <BR>";
    		str=str + "Sex: "+ obj.sex(obj.sex_id) +" <BR>";
    		str=str + "Power: "+ obj.pw;
    		str=str + "<BR><BR>";
    		return str;
    	}
    }
    
    //*********** 継承関数 extnds() **********************************
    // 親クラスcharaのメンバを子クラスにコピーする。
    //******************************************************************
    function extnds(subclass, superclass){
    	subclass.name=superclass.name;	
    	subclass.age=superclass.age;
    	subclass.sex_id=superclass.sex_id;
    	subclass.sex=superclass.sex;
    }
    //-->
    </SCRIPT>
    

     子クラスを、親クラスを作ったときと同様に作成し、このextnds()を呼べば、そのクラスは子クラスになります。やらなければ当たり前ですがただの他人クラス(なんて言いかたしないけど)です。
     呼び方はwizardクラス、soldierクラスの青字の部分の通り、

         c=new chara(name, age, sex_id);     (←親クラス)
         extnds(this, c);                (extnds(子クラス, 親クラス); thisはそのクラス自体)

     というような感じになります。
     ついでですが、そろそろ表示する内容が長くなってきてしまったので、「出力する内容を見やすく整えて一つの文字列として返す」というメソッドstts()も、wizardとsoldierクラスの中に作ってみようと思います。
     子クラスのメソッドの作り方も、前回と同様です。
     その結果がこうなります。

     魔術士のパラメータにはMagicPowerが、戦士のパラメータにはPowerが表示されました。


【2・クラス】 【INDEX】 【4・継承(2)】