FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

オブジェクト指向入門 第8回 リファクタリング

今回は、前回のパックマンプログラムにのんびり動く緑モンスターを追加したいと思います。
追加後の画面出力は以下のとおりです。

Pacman.gif
ここからダウンロードできます。


他のモンスターの様にGetSkinColor操作とGetAlgorithm操作をオーバーライドするだけでは、上図の様には表示されません。
コンパスプログラムの時とは違い、派生クラスを追加するだけで対応するのは難しそうです。

ただ、不可能ではありません。例えば、次のようにGetAlgorithm操作で文言出力を済ませ、基本クラスの出力処理を骨抜きにしてしまえば実現できます。

        /// <summary>
        /// 緑モンスターの実装
        /// </summary>
        class GreenMonster : Monster
        {
            protected override ConsoleColor GetSkinColor()
            {
                return ConsoleColor.Green;
            }

            protected override string GetAlgorithm()
            {
                // ここでアルゴリズム文言を出力してしまう。
                foreach (char c in "のんびり動きます。")
                {
                    System.Threading.Thread.Sleep(500);
                    Console.Write(c);
                }

                // 基本クラスには改行だけさせる。
                return "";
            }
        }

しかし、この方法は「機能追加は、派生クラスの追加」という開発方針を守るために、「GetAlgorithm操作はアルゴリズム文言を返すだけ」という設計方針を無視しています。

このように設計方針を無視してしまうと、クラス名や操作名から処理内容を予測しにくいため、可読性が低下してしまいます。
そして何より、派生クラスの追加を、アイデアや発想力が必要な難しい作業にしてしまっています。

派生クラスを考える事が難しい作業なら、「機能追加は、派生クラスの追加」という方針も効果は半減してしまいます。
このような時は、素直に基本クラスを修正します。

基本クラスを修正する際にも方針があります。

  ①基本クラスの機能自体は変更しない。機能の実現方法/クラスの内部構造だけ変える。
  ②「機能追加は、派生クラスの追加で対応」の土台作りをする。

①は、クラスを利用している処理や、既にある派生クラスに悪影響を与えないためのものです。変更が変更を呼ぶといった変更の連鎖を防ぎます。
②は、「あの時このように設計していれば、簡単に派生クラスを追加できたのに」という設計を、これから行おうという事です。それさえ行ってしまえば、後は簡単な作業になります。

つまり、リファクタリングしましょう、という事です。
最初から未来を見通して設計できれば良いのですが、預言者にならない限り不可能です。
リファクタリングは、不運にも結果的に過ちとなってしまった過去を直してくれる強力なテクニックです。


さて、今回のネックは派生クラスで文言出力方法を変更できない事なので、それを行えるように基本クラスであるMonsterクラスを変更します。

        /// <summary>
        /// モンスタークラス(抽象クラス)
        /// </summary>
        abstract class Monster : IMonster
        {
            // 移動処理
            void IMonster.Move()
            {
                // 似ている部分(重複コード)を共通化
                Console.ForegroundColor = GetSkinColor();
                Draw(GetAlgorithm());   // 出力処理をメソッド化
                Console.ResetColor();
            }

            // 出力処理をメソッド化
            protected virtual void Draw(string algorithm)
            {
                Console.WriteLine(algorithm);
            }

            // 非なる部分は抽象操作とし、派生クラスでオーバーライド
            protected abstract ConsoleColor GetSkinColor();
            protected abstract string GetAlgorithm();
        }

これで後は「機能追加は、派生クラスの追加」となりました。また、この変更はMove操作の内部構造を変えただけなので、既存の他の処理/クラスに悪影響は一切ありません。

いよいよ派生クラスを追加します。
(個人的な気分の問題で、のろまモンスター(抽象クラス)を追加して、その派生クラスとして緑モンスターを追加しています。)

        /// <summary>
        /// のろまモンスター(抽象クラス)
        /// </summary>
        abstract class SlowMonster : Monster
        {
            protected override void Draw(string algorithm)
            {
                // 一文字ずつ出力
                foreach (char c in algorithm)
                {
                    System.Threading.Thread.Sleep(GetSpeed());
                    Console.Write(c);
                }

                Console.WriteLine();    // 改行
            }

            protected virtual int GetSpeed() { return 500; }
        }

        /// <summary>
        /// 緑モンスターの実装
        /// </summary>
        class GreenMonster : SlowMonster
        {
            protected override ConsoleColor GetSkinColor()
            {
                return ConsoleColor.Green;
            }

            protected override string GetAlgorithm()
            {
                return "のんびり動きます。";
            }
        }

後は、メイン関数で緑モンスターを生成するだけです。

        /// <summary>
        /// メイン関数
        /// </summary>
        static void Main(string[] args)
        {
            // モンスター軍団作成(緑を追加)
            IMonster[] monsters =
            {
                new RedMonster(),
                new PinkMonster(),
                new BlueMonster(),
                new OrangeMonster(),
                new GreenMonster()
            };

            ・・・
            (省略)
            ・・・
        }


■まとめ
  ・簡単に派生クラスを追加できないときは、簡単に追加できるようにリファクタリングする。

■ご参考
  ・マーチン・ファウラー「リファクタリング : プログラミングの体質改善テクニック」


ポリモーフィズムの話はこれで一段落して、次回は違う事を書いてみたいと思います。
スポンサーサイト

テーマ : ソフトウェア開発
ジャンル : コンピュータ

コメントの投稿

管理者にだけ表示を許可する

プロフィール

きっぽ

Author:きっぽ

カテゴリ
最新記事
最新コメント
最新トラックバック
月別アーカイブ
FC2カウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。