機能を再編成する
抽出方法
関数が長すぎる場合、またはユーザーにその目的を理解させるためにコメントが必要なコードの場合は、
extractメソッドを使用する必要があります。extractメソッドは、関数の粒度と関数名を調整する必要があります。
すべて:
- 元の関数の粒度を調整します。新しい関数を作成し、関数名を調整します。
- 抽出したコードをソース関数から新しい関数にコピーします。
- ローカル変数と一時変数を処理します(スコープはソース関数に限定されます)(tempをクエリに置き換えます/メソッドをメソッドオブジェクトに置き換えます);
- 「洗練された関数のコードセグメントにのみ使用される」一時変数があるかどうかを確認し、ある場合は、ターゲット関数で一時変数として宣言します。
- 洗練されたコードセグメントをクエリとして処理し、結果をソース関数に関連する一時変数にコピーできるかどうかを確認します。または、変更された一時変数が複数ある場合は、最初に分割一時変数を使用して一時変数を処理します。
- 洗練されたコードセグメントで使用する必要のある変数をパラメーターとして新しい関数に渡します。
- 洗練されたコードセグメントへの呼び出しを、ターゲット関数内のターゲット関数への呼び出しに置き換えます。
利点:関数の粒度を減らし、関数の再利用の機会を改善します。高レベルの関数を一連のコメントのように読み取らせます。
インライン方式
関数の本体がその名前と同じくらい明確で理解しやすい場合は、それを呼び出すターゲット関数に関数本体を追加し、同時に関数を削除します。
インライン法のもう1つの用途は、怪我をしたときに一部の関数が不当に編成され、これらの関数がインライン法でインライン化され、次に抽出法で妥当な新しい関数が抽出されることです。
一般に、関数の抽出方法でリファクタリングした後、そのような状況が発生した場合は、抽出をキャンセルする必要があることがわかります。「抽出された関数の名前とその本体のロジックは明確で理解しやすい」
すべて:
- 関数をチェックして、ポリモーフィックでないことを確認します。
- この関数の呼び出されたすべてのポイントを見つけます。
- この関数の呼び出されたすべてのポイントを関数本体に置き換えます。
インライン温度(インライン一時変数)
インライン一時変数、例:
// old
int c = 2, d = 3;
int a = c *d;
return a;
// new
int c = 2, d = 3;
return c * d;
すべて:
- 一時変数への代入ステートメントをチェックして、等号の右側の式に副作用がないことを確認します。
- 一時変数がfinalとして宣言されていない場合は、finalとして宣言します(この手順では、変数が1回だけ割り当てられているかどうかを確認します)。
- この一時変数を見つけます(exprを返し、一時変数を削除します)
インライン温度の概念は非常に単純です、私はすでにこのプログラミングの概念を持っています
tempをqueryに置き換えます(一時変数をqueryに置き換えます)
ソース関数の特定の一時変数は、特定のコードセグメントの論理実行結果を格納し、extractメソッドを使用してコードセグメントを新しい関数に抽出し、対応する一時変数を使用して新しいの戻り値を受け入れます。関数;
すべて:
- 一度だけ割り当てられる一時変数を見つけます。
- 一時変数の計算ロジックを新しい関数に改良します。
- インライン一時概念を使用して、再構築を完了します。
「リファクタリング」セクション6.4、電子書籍147ページ、紙の本123ページ、シンプルですが素晴らしい例があります。
説明変数を紹介します
複雑な式の結果は、明確な名前の一時変数に格納されます。たとえば、より長いアルゴリズムでProなどの一時変数を使用すると、アルゴリズムの論理的意味をより適切に解釈できます。
この再構成手法は、抽出方法に似ています。ローカル変数が多い場合は、導入説明変数の使用を検討してください。一時変数が少ない場合は、extractメソッドの方が見栄えが良くなります。
すべて:
- 最終変数を宣言し、複雑な式の結果をこの変数に割り当てます。
- この一時変数で表される複素式が使用されているすべての場所を置き換えます。
一時変数を分割する
「結果変数の循環収集」、「結果収集変数」、「いくつかの複雑な中間計算結果は一時変数に格納されます」、これらは変数が複数回割り当てられる典型的な状況です。各変数が個別の責任を負うという原則に従う必要があります。一時変数が関数内で複数の責任を負う場合、この責任は複数の一時変数間で共有する必要があります。
すべて:
- 分解する一時変数の最初の割り当てで、その名前を変更します。
- 再度割り当てられる場所を除いて、この一時変数が使用されるすべての場所を置き換えます。
- 分解される一時変数の2番目の割り当てで、新しい最終変数を宣言します。
- 分解する一時変数の2番目の割り当てで、それを新しい一時変数に割り当てます。
パラメータへの割り当てを削除します(パラメータへの割り当てを削除します)
関数から渡される変数パラメーターをパラメーターで変更することがよくあることに気づきましたか?
int discount (int inputVal, int quantity, int yearToDate){
if(inputVal > 50) inputVal -= 2;
}
上記のコードでは、関数で渡されたパラメーターが割り当てられています。
次のコードでは、この状況が変更されています。
実際、このリファクタリング方法は少し冗長に見えますが、参照として渡されるパラメーターの場合、特に一部の弱い型の言語では、元の変数の値を変更する可能性が非常に高いため、この点に注意する必要があります。注この変数を使用する場合=
、割り当てを使用するだけでは必ずしも正しいとは限りません。
int discount (int inputVal, int quantity, int yearToDate){
int result = inputVal;
if (inputVal > 50) result -= 2;
}
すべて:
- 一時変数を作成します。
- 着信パラメーターを一時変数に割り当てます(高Pythonリストなど、一部の弱い型の言語では、リスト生成を使用して一時変数を割り当てる必要がある場合があります)
メソッドをメソッドオブジェクトに置き換えます(関数を関数オブジェクトに置き換えます)
巨大な関数の場合、抽出法は再構成に使用できず、一時変数が散在しているため、現時点では、現在の再構成法を再構成に使用することを検討できます。
関数を関数オブジェクトに置き換えるとは、クラス方式で関数を再構築することを意味します。この関数で使用される一時変数がこのクラスの属性になります。このとき、extractメソッドの再構築メソッドを使用して関数を再構築します。大きな関数複数の小さな関数に分解されます。
すべて:
- 新しいクラスを作成し、読みやすくわかりやすいクラス名を付けます。
- 新しいクラスに最後のフィールドを作成して、元の大きな関数が配置されているオブジェクトを保存します。このオブジェクトをソースオブジェクトと呼びます。同時に、元の関数の一時変数とパラメーターごとに、対応するフィールドが新しいクラスに作成されて保存されます。
- ソースオブジェクトと元の関数のすべてのパラメーターをパラメーターとして受け入れるコンストラクターを新しいクラスに作成します。
- 新しいクラスにcompute()関数を作成します
- 元の関数のコードをcompute()関数に割り当てます。ソースオブジェクトの関数を呼び出す必要がある場合は、ソースオブジェクトフィールドから呼び出してください。
- 古い関数の本体を次の文に置き換えます:「新しいクラスにアピールする新しいオブジェクトを作成し、その中でcompute()関数を呼び出します」
例:160ページの電子書籍と136ページの物理的な本。一目で理解できる非常に強力な例があります。例は記事の最後に補足されています。
代替アルゴリズム
特定のアルゴリズムをより明確なアルゴリズムに置き換えます。
すべて:
- 置換アルゴリズムを準備する
- 既存のテストでは、アピールアルゴリズムが実行され、結果が元のアルゴリズムと一致する場合、再構成は終了します。
メソッドをメソッドオブジェクトに置き換える范例
class Account
{
int Gamma(int inputVal, int quantity, int yearToDate)
{
int importantValue1 = inputVal * quantity + Delta();
int importantValue2 = inputVal * yearToDate + 100;
if (yearToDate - importantValue1 > 100)
{
importantValue2 -= 20;
}
int importantValue3 = importantValue2 * 7;
//and so on...
return importantValue3 - 2 * importantValue1;
}
public int Delta()
{
return 100;
}
}
この関数を関数オブジェクトに変換するには、最初に新しいクラスを宣言します。新しいクラスでは、元のオブジェクトを保存するためのフィールドが提供され、関数の各パラメーターと各一時変数を保存するためのフィールドも提供されます。
class Gamma
{
private readonly Account _account;
private readonly int _inputVal;
private readonly int _quantity;
private readonly int _yearToDate;
private int _importantValue1;
private int _importantValue2;
private int _importantValue3;
}
次に、コンストラクターを追加します。
public Gamma(Account account, int inputVal, int quantity, int yearToDate)
{
_account = account;
_inputVal = inputVal;
_quantity = quantity;
_yearToDate = yearToDate;
}
次に、元の関数をCompute()に移動します。
public int Compute()
{
_importantValue1 = _inputVal * _quantity + _account.Delta();
_importantValue2 = _inputVal * _yearToDate + 100;
if (_yearToDate - _importantValue1 > 100)
{
_importantValue2 -= 20;
}
_importantValue3 = _importantValue2 * 7;
//and so on...
return _importantValue3 - 2 * _importantValue1;
}
完全なガンマ関数は次のとおりです。
class Gamma
{
private readonly Account _account;
private readonly int _inputVal;
private readonly int _quantity;
private readonly int _yearToDate;
private int _importantValue1;
private int _importantValue2;
private int _importantValue3;
public Gamma(Account account, int inputVal, int quantity, int yearToDate)
{
_account = account;
_inputVal = inputVal;
_quantity = quantity;
_yearToDate = yearToDate;
}
public int Compute()
{
_importantValue1 = _inputVal * _quantity + _account.Delta();
_importantValue2 = _inputVal * _yearToDate + 100;
if (_yearToDate - _importantValue1 > 100)
{
_importantValue2 -= 20;
}
_importantValue3 = _importantValue2 * 7;
//and so on...
return _importantValue3 - 2 * _importantValue1;
}
}
最後に、古い関数を変更して、完了したばかりの関数オブジェクトにその作業を委任します。
int Gamma(int inputVal, int quantity, int yearToDate)
{
return new Gamma(this, inputVal, quantity, yearToDate).Compute();
}
これがこの再建の基本原則です。その利点は次のとおりExtract Method
です。パラメーターの受け渡しを気にせずに、Compute()関数を簡単に実行できるようになりました。
たとえば、Computeを次のようにリファクタリングしました。
public int Compute()
{
_importantValue1 = _inputVal * _quantity + _account.Delta();
_importantValue2 = _inputVal * _yearToDate + 100;
GetImportantThing();
_importantValue3 = _importantValue2 * 7;
//and so on...
return _importantValue3 - 2 * _importantValue1;
}
void GetImportantThing()
{
if (_yearToDate - _importantValue1 > 100)
{
_importantValue2 -= 20;
}
}