(前頁)<<      >>(次頁)          (Up)

2章 準備勉強
2.2 FORTRANのモデルをC#で制作する (その2)



 作成した2つのモデルを較べてみると非常によく似た処理構造を持っていることが解ります。

    


 そこで、似た処理構造のモデルを一つの概念として捉えてみます。 すなわち、「DynamicWaveモデル」と 「KinematicWaveモデル」を包含する上位概念として「河川モデル」を定義します。 「河川モデル」は上記の処理構造を持つものとします。 この様に上位概念クラスを定義すると、共通な処理構造を上位クラスに任せて、下位クラスは上位クラスに定義されている処理を使用することができるようになります。 このような関係を 「派生」関係を持つといい、下図の様に表します。(△の印は 派生関係にあることを示します。)
    

 

 オブジェクト指向において、派生関係は非常に重要な概念なので、別の喩えを用いて説明します。
 
 オブジェクト指向の説明用に 「都会人生活をプログラムする」という課題を考えてみます。 登場人物はある家庭の人間を考え 夫、妻、息子、娘が居るとします。  すると、父、母、息子、娘、それぞれ 仕事も勤務先も違う思いますが、一般的な家庭に属する人間の生活が 「起床」、「朝食」、「通勤」、「仕事(AM)」、「昼食」、「仕事(PM)」、「帰宅」、「夕食」、「寛ぎ」、「就寝」として行われていると考えることができます。 そこで、一つの上位概念として 都会人(Human)というクラスを考え、その派生概念として、「夫」、「妻」、「息子」、「娘」の各クラスが存在するとします。
     
     

 また、クラス「人」の行う行為(=メソッド)は、下図のように  「起床」、「朝食」、「出勤」、「仕事(AM)」、「昼食」、「仕事(PM)」、「帰宅」、「夕食」、「寛ぎ」、「就寝」であるとします。 
      
 ここで、上記「人」の具体的クラスを C#でコーディングしてみます。 
 これまでと同様に コンソールアプリケーションプロジェクトを作成し、クラス「Human」を追加します。 メソッドとして、「起床」、「朝食」、「出勤」、「仕事」、「昼食」、「帰宅」、「夕食」、「寛ぎ」、「就寝」 を実装しますが、具体的な処理内容は単に コンソールにメソッドの内容を示す文字列を表示するだけとします。
namespace FamilyProg
{
    public class Human
    {
        public virtual void WakeUp()
        {
            Console.WriteLine("起床");
        }
        public virtual void Breakfast()
        {
            Console.WriteLine("朝食");
        }
        public virtual void GoToOffice()
        {
             Console.WriteLine("通勤");
        }
        public virtual void Work()
        {
            Console.WriteLine("仕事");
        }
        public virtual void Lunch()
        {
            Console.WriteLine("昼食");
        }
        public virtual void ComeHome()
        {
            Console.WriteLine("帰宅");
        }
        public virtual void Supper()
        {
            Console.WriteLine("夕食");
        }
        public virtual void Relax()
        {
            Console.WriteLine("寛ぐ");
        }
        public virtual void GoToBed()
        {
            Console.WriteLine("就寝");
        }
    }
}

 メソッドに 「virtual」 の宣言がありますが、それは後で説明します。
 次に、クラス「人間」の営みを メインプログラムから呼び出します。

    class Program
    {
        static void Main(string[] args)
        {

            Human csHman = new Human();

            Console.WriteLine("★一日の開始");
            // 一日の出来事
            csHman.WakeUp();
            csHman.Breakfast();
            csHman.GoToOffice();
            csHman.Work();
            csHman.Lunch();
            csHman.Work();
            csHman.ComeHome();
            csHman.Supper();
            csHman.Relax();
            csHman.GoToBed();

            Console.WriteLine("★一日の終了");
        }
    }


 実行させるとコンソールに下記の様に表示されます。
    


 ここで、派生クラス「夫: Husband 」を制作してみます。
 新しく クラスを追加し、Humanからの派生である事を示すために クラス名の後に ”: Human” と記述します。
     public class Husband : Human

 次に、親クラスである「人」クラスのメソッドのうち、 「夫」クラス特有の処理だけを下に示すように実装します。
    public class Husband : Human
    {
        public override void GoToOffice()
        {
            Console.WriteLine(">夫< 会社へ出勤します");
        }
        public override void Work()
        {
            Console.WriteLine(">夫< 会社で仕事をします");
        }
        public override void Lunch()
        {
            Console.WriteLine(">夫< 社員食堂で食べます");
        }
        public override void Relax()
        {
            Console.WriteLine(">夫< 読書をして過ごします");
        }
    } 
 Husbandクラスの GoToOffice, Work, Lunch, Relax の各メソッドは 親クラスの GoToOffice, Work, Lunch, Relax と同じ名称です。

 ここで、メインプログラムの
 new Human(); を new Husband(); に変更します。
 それ以外は、メイン側の処理を変更していません。

        static void Main(string[] args)
        {

            //Human csHman = new Human();  <-- コメントアウト
            Human csHman = new Husband();    //<-- 生成する実体をHusbandにする
 
            Console.WriteLine("★一日の開始");
            // 一日の出来事
            csHman.WakeUp();
            csHman.Breakfast();
            csHman.GoToOffice();
            csHman.Work();
            csHman.Lunch();
            csHman.Work();
            csHman.ComeHome();
            csHman.Supper();
            csHman.Relax();
            csHman.GoToBed();

            Console.WriteLine("★一日の終了");
        } 

 
 早速このプログラムを実行してみます。
    

 実行結果を見ると、Husbandクラスに実装されていない WakeUp, Breakfast, ComeHome, Supper, GoToBed は 親クラス(Humanクラス)の処理が行われていますが、 Husbandクラスに実装されている GoToOffice, Work, Lunch, Relax は、子クラス側(Husbandクラス)のメソッドが実行されています。
 メインプログラムから見ると、 Humanで定義した変数 csHman に対して各種メソッドを呼び出しています。 つまり、メインプログラムは csHmanを Humanだと認識してプログラムを実行しています。  しかしながら、インスタンスの生成時には Humanの派生クラスである Husband を new しています。 すなわち 実際のインスタンスとしては Husband が生成されています。 従って、 実際にプログラムが実行される時には派生クラス(Husband)側のメソッドが呼ばれます。

    ・メインプログラムは  変数:csHman を 親クラス:Human として認識している。
    ・実際の実行時には Human の派生である Husband が実行されている。 

 Husband で実装していないメソッドについては、親クラスの処理が呼ばれます。 この事は 子クラスは (子クラス側で固有な処理が必要なければ)親クラスの処理がそのまま財産として使用できる事を示しています。 換言すると、子クラス側は、自分の固有メソッドのみ実装すれば 十分という事を示しています。 
 このような性質は オブジェクト指向の大きな特徴で 「継承」と呼びます。

 親クラスが準備したメソッドに対して、子クラス側で固有なメソッドを実装しても良いという事を示す宣言として 親クラスは 「 virtual 」と宣言しておく必要があります。 また派生クラス側は 「 override 」 と宣言して親クラスの処理を引き取ります。(オーバーライドすると言います) この様に 親クラスと子クラスで同じメソッド名であっても 異なる処理が行える性質を 「多態性」と呼び、オブジェクト指向の重要な性質です。

 次に、「息子:Son」クラスを作成します。

    public class Son : Human
    {
        public override void GoToOffice()
        {
            Console.WriteLine(">息子< 大学へ通います");
        }
        public override void Work()
        {
            Console.WriteLine(">息子< 大学で講義を聴講します");
        }
        public override void Lunch()
        {
            Console.WriteLine(">息子< 学生食堂で食べます");
        }
        public override void Relax()
        {
            Console.WriteLine(">息子< 宿題をします");
        }
    } 

 また、メインプログラムでは インスタンス生成する部分を次のように変更します。

            //Human csHman = new Human();  // <-- コメントアウト
            //Human csHman = new Husband();  // <-- コメントアウト
            Human csHman = new Son();     // <-- 生成する実体をSonにする

            Console.WriteLine("★一日の開始");
        。。。。。。。。。。 

 

 実行結果を次に示します。
    
 やはり、 派生クラスで実装したメソッドのみが、固有の処理を行なっています。
           ( 制作したプログラム: (FamilyProg.lzh) )
 
 親クラス「人」と、派生クラスの「夫」「息子」は 同じ名称のGoToOffice, Work, Lunch, Relax メソッドを実装しています。 しかしながら 「多態性」の性質により new するクラスによって 異なる処理が実行されています。



 これで オブジェクト指向の重要な性質が揃いました。すなわち、「情報の隠蔽」、「継承」、「多態性」です。 オブジェクト指向でプログラムを作成することで、次のようなメリットが生じます。
<呼び出す側から見ると>
 ・上位概念クラスを定義し、上位概念クラスとしての実行手順を規定していれば、細かな処理の違いを意識する必要がない。 また、フレームワークを壊さない限り、新しい状況が発生しても派生クラスを新たに制作すれば対処できる。
<派生側から見ると>
 ・フレームワーク全てに対応する処理を実装しなくても、自分の固有処理だけを実装すれば フレームワーク内に処理を組み入れることができる。


 さて オブジェクト指向の 派生関係の説明がひと通り終わったので 水理モデル作成の話に戻ります。


(前頁)<<      >>(次頁)

初版:2013/06/12 |