連載
|
|
|
■リファクタリング1:Strategy階層のトップに位置するクラスの作成
最初に「価値」を出力するためのアルゴリズムをクラスに分離して独立させよう。具体的にはTextFormatterクラスを作成する。このクラスは、出力のためのクラスの階層(Strategy階層)のトップに位置することになるクラスである。
|
|
新しく作成したTextFormatterクラス(C#) | |
このコードをコンパイルして冒頭で作成したテストを実行する。もちろんコンパイルとテストは正常に完了するはずだ。
■リファクタリング2:「メソッドの移動(F)」によるアルゴリズムの集合の分離
「価値」を出力するためのTextFormatterクラスが存在する状態では、「価値」を出力するという実行責任(アルゴリズム)が、XpValuesクラスに割り当てられたままになっているのは不適切だ。
そこで「メソッドの移動(F)」を行い、この責任を適切にTextFormatterクラスに割り当て直す。
最初の準備として、XpValuesクラスのFormatメソッドとその内部で呼び出されているGetTitleメソッドをTextFormatterクラスへと移動しよう。
|
|
XpValuesクラスから移動されたTextFormatterクラスのFormatメソッドとGetTitleメソッド(C#) | |
TextFormatterクラスへと移動した上記のFormatメソッドとGetTitleメソッドの内部では、移動元であるXpValuesクラスのフィールドを参照している個所があったため、このままではコンパイルが通らない。
そこでFormatメソッドとGetTitleメソッドにはXpValuesクラスのインスタンス自体をパラメータとして引き渡すことにしよう。つまりXpValuesクラスのフィールドの参照はすべてXpValuesクラスのプロパティの参照へと置き換えるわけだ。
またGetTitleメソッドは、このままのメソッド名ではTextFormatterのタイトルを取得するメソッドだと勘違いしてしまうので、「メソッド名の変更(F)」を行い、GetTitleメソッドの名前をより適切な名前であるGetTitleForメソッドへと変更する。
具体的には次のようなコードとなる。
|
|
XpValuesクラスのフィールドの参照がプロパティの参照へ置き換えられたTextFormatterクラスのFormatメソッドとGetTitleメソッド(C#) | |
このTextFormatterクラスのFormatメソッドでは、XpValuesクラスのFormatTypeプロパティの値によって条件分岐を行うように変更したが、XpValuesクラスにはFormatTypeプロパティが存在しないのでこれを追加する。
次に、中身をTextFormatterクラスへ移動したXpValuesクラスのFormatメソッドでは、TextFormatterクラスのインスタンスを生成して、そのFormatメソッドを呼び出すように変更する。
これらを行ったのが次のコードだ。
|
|
XpValuesクラスにおける移動元Formatメソッドの変更とFormatTypeプロパティの追加(C#) | |
ここまでに作成したコードをコンパイルして、再びテストを実行しよう。今度は正常にコンパイルが完了し、テストも正常にパスするはずだ。
これで「メソッドの移動(F)」が完了した。
その結果として「価値」を出力するためのアルゴリズムの集合がXpValuesクラスから分離され、Strategy階層のトップとなるTextFormatterクラスへ移動された。しかしTextFormatterクラスのFormatメソッドには、条件記述や重複したアルゴリズムなどの問題が依然として残されたままになっている。従ってさらにリファクタリングが必要だ。
以降のリファクタリングの計画を次に示す。
TextFormatterクラスのFormatメソッド内には、XpValuesクラスのFormatTypeプロパティの値によって異なる振る舞いをするアルゴリズムの集合が存在する。そこで各アルゴリズムをカプセル化して交換可能にするために、TextFormatterクラスの派生クラスを作成してFormatメソッドをオーバーライドさせる。
つまりStrategy階層を構築して各Strategyに異なるアルゴリズムを持たせ、それらを切り替えることで異なる振る舞いを実現させる。このようにして条件記述を書かなくてもいいようにしてしまうのだ。
■リファクタリング3:「Extract Parameter(K)」(パラメータの抽出)による各Strategyのインスタンス切り替えの準備
XpValuesクラスのFormatメソッドで行っているTextFormatterクラスのインスタンス生成(上記コードの「new TextFormatter()」の部分)の記述がハードコーディングされたままだと、これから作成することになる各Strategy(TextFormatterクラスの派生クラス)のインスタンスを切り替えることができなくなる。
そこで、ハードコーディングされているTextFormatterクラスのインスタンス生成の記述をやめ、あらかじめ生成したインスタンスをフィールド(下記コードの「private TextFormatter formatter」の部分)により保持するようにする。実際のインスタンス化はプライベート・コンストラクタ内で行う。
|
|
XpValuesクラスにおけるTextFormatter型フィールドの追加(C#) | |
またコンパイルしてテストを実行しよう。正常にテストが通るはずだ。
各Strategyのインスタンスを保持するフィールドが作成されたので、次はインスタンスを切り替える方法を考えよう。今回は、あらかじめ外部で生成された各StrategyのインスタンスをXpValuesクラスのプライベート・コンストラクタに引き渡す方法を採用する。
そこで「Extract Parameter(K)」(パラメータの抽出)を行い、XpValuesクラスのプライベート・コンストラクタで生成されているTextFormatterクラスのインスタンスを、コンストラクタのパラメータとして抽出する。
また、TextFormatterクラスのインスタンス生成は、CreatePlainTextFormatXpValuesメソッドとCreateHtmlTextFormatXpValuesメソッドで行うことにする。
具体的なコードは次のとおりだ。
|
|
プライベート・コンストラクタにTextFormatter型のパラメータが追加されたXpValuesクラス(C#) | |
再度、コードをコンパイルしてテストを実行してみよう。これも正常に実行できるはずだ。
これで「Extract Parameter(K)」が完了した。
その結果として、各Strategyのインスタンス切り替えの準備が整った。以降のリファクタリングで、CreatePlainTextFormatableXpValuesメソッドではプレーン・テキスト形式で価値の出力を行うStrategyが生成され、CreateHtmlTextFormatableXpValuesメソッドではHTML形式で価値の出力を行うStrategyが生成されることになる。
それでは次に、条件記述に関する問題を解決するための最後のリファクタリングを行うことにしよう。
INDEX | ||
.NETで始めるデザインパターン | ||
第3回 リファクタリングにより導き出すStrategyパターン | ||
1.リファクタリング前の設計 | ||
2.リファクタリングによるStrategyパターンの適用 | ||
3.ポリモーフィズムによる条件記述の置き換え | ||
「.NETで始めるデザインパターン」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|