プログラミングBlog

C#でオブジェクト指向①

オブジェクト指向の抽象化

目的:朝のルーティンをオブジェクト指向の抽象化を使って実装していく。

まず、実際にコードを書いていく前にルーティーンを考えました。
起床、目覚まし止める、歯磨き、着替え、トイレ、朝ごはん.....etc 書きだしたらきりがないので抽象化してきます。

抽象可とは・・・思考における手法のひとつで、対象から注目すべき要素を重点的に抜き出して他は捨て去る方法である

抽象化 - Wikipedia wiki調べ。

というわけでまとめてみました。

平日の朝のルーティー

起床、準備、出勤

休日の朝のルーティー

起床、準備、まったり


イメージとしては基底クラスにこれらの3つのふるまいを抽象化して、
継承先に実装した処理を書いていく。
なので用意するものは基底クラス、休日クラス、平日クラス
基底クラスに起床、準備、出勤、まったり。 処理の開始メソッド。分岐メソッド。
このような感じでしょうか。
実際に書いていきます。

基底クラス

基底(Base)となるクラスを作成していきます。

  • abstractは継承先で必ず実装する。

  • virtualは継承先で実装するかしないかを選択できる。


出勤とまったりメソッドについては 平日だけど午前休だとか、休日だけどまったりできない日とかあるかもしれないので変更できるようvirtualにしてみました。

さきほどまとめた4つのメソッド 起床、準備、、出勤、まったり

        // 起床
        protected abstract void GetUp();

        // 準備
        protected abstract void Preparation();

        // 出勤
        protected virtual void Commuting()
        {
            Console.WriteLine("出勤します。");
        }

        // まったり
        protected virtual void Relax()
        {
            Console.WriteLine("まったりします。");
        }

これらの共通処理を実行するメソッド を書いていきます。
まずルーティング分岐とルーティンスタートメソッド2種類用意。
後々、ルーティンが増える??昼とか夜とか。。。かもしれないので分岐メソッドも定義してみました。
flgを立てて休日か平日によって処理が変わるようにしています。

分岐メソッド。 処理の開始メソッド。

        /// <summary>
        /// ルーティーン分岐
        /// </summary>
        public static void RoutinWork(bool isFolidayFlg)
        {
            if (isFolidayFlg)
            {
                new HolidayRoutin().Start(isFolidayFlg);
            }
            else
            {
                new WeekdaysRoutin().Start(isFolidayFlg);
            }
        }

        /// <summary>
        /// ルーティーンスタート
        /// </summary>
        public void Start(bool isFolidayFlg)
        {
            // 起床
            GetUp();

            // 準備
            Preparation();

            if (isFolidayFlg)
            {
                // まったり
                Relax();
            }
            else
            {
                // 出勤
                Commuting();
            }
        }

基底クラス

 /// <summary>
    /// ルーティーン基底クラス
    /// </summary>
    public abstract class RoutinWorkBase
    {

        /// <summary>
        /// ルーティーン分岐
        /// </summary>
        public static void RoutinWork(bool isFolidayFlg)
        {
            if (isFolidayFlg)
            {
                new HolidayRoutin().Start(isFolidayFlg);
            }
            else
            {
                new WeekdaysRoutin().Start(isFolidayFlg);
            }
        }

        /// <summary>
        /// ルーティーンスタート
        /// </summary>
        public void Start(bool isFolidayFlg)
        {
            // 起床
            GetUp();

            // 準備
            Preparation();

            if (isFolidayFlg)
            {
                // まったり
                Relax();
            }
            else
            {
                // 出勤
                Commuting();
            }
        }

        // 起床
        protected abstract void GetUp();

        // 準備
        protected abstract void Preparation();

        // 出勤
        protected virtual void Commuting()
        {
            Console.WriteLine("出勤します。");
        }

        // まったり
        protected virtual void Relax()
        {
            Console.WriteLine("まったりします。");
        }
    }

継承先のクラス

休日クラスと平日クラスにさきほど作成した基底クラスを継承してみましょう。
さきほど抽象化した起床、準備メソッドを実装していきます。
ここでオブジェクト指向ポリモーフィズムがでてきます。

ポリモーフィズムとは・・・プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属することを許すという性質を指す。
アドホック多相(Ad hoc polymorphism):ある関数が、異なる型の引数に対してそれぞれ異なる実装を持つ場合。多くのプログラミング言語で関数の多重定義としてサポートされる。
パラメータ多相(Parametric polymorphism):コードが特定の型を指定せずに書かれることで、さまざまな型に対して透過的に使用できる場合。オブジェクト指向言語ではジェネリクスジェネリックプログラミングとしても知られる。関数型言語の分野ではパラメータ多相のことを指して単に多相性と呼ぶ場合がある。
部分型付け(Subtyping):部分型多相(subtype polymorphism)や包含多相(inclusion polymorphism)とも。共通の上位型をもつ複数の型を、1つの名前で扱う場合。オブジェクト指向言語の分野では部分型付けのことを指して単にポリモーフィズムと呼ぶ場合がある。

ポリモーフィズム - Wikipedia
Wiki調べ。

奥が深すぎてよくわかりませんが、オブジェクトのメソッドに多様なふるまいを持たせることなので、 overrideで処理の上書きをしていきます。

休日のルーティンクラス

    /// <summary>
    /// 休日のルーティン
    /// </summary>
    public class HolidayRoutin : RoutinWorkBase
    {
        protected override void GetUp()
        {
            Console.WriteLine("7:00に起きる。");
        }

        protected override void Preparation()
        {
            Console.WriteLine("7:10分から準備します。");
        }
    }

平日のルーティンクラス

    /// <summary>
    /// 平日のルーティン
    /// </summary>
    public class WeekdaysRoutin : RoutinWorkBase
    {
        protected override void GetUp()
        {
            Console.WriteLine("7:30に起きる。");
        }

        protected override void Preparation()
        {
            Console.WriteLine("7:40分から準備します。");
        }
    }

実行

     RoutinWorkBase.RoutinWork(true);
     RoutinWorkBase.RoutinWork(false);

結果

     7:00に起きる。
     7:10分から準備します。
     まったりします。
     7:30に起きる。
     7:40分から準備します。
     出勤します。

出勤を休日にする場合。

もし出勤が無くなって休日になった場合はCommuting(出勤)メソッドでをRelax()を呼ぶようにすればコードの修正がここだけで終わります。
virtualの場合は柔軟に対応できるので便利ですね。
ちょっと強引な気もするのでもっと良い方法ないか、デザインパターンの勉強もしていきたいと思います。

        protected override void Commuting()
        {
            base.Relax();
        }

結果

     7:00に起きる。
     7:10分から準備します。
     まったりします。
     7:30に起きる。
     まったりします。
     出勤します。