この小さな投稿では、「リファクタリング」というタイトルの章の1つである「C#でのアジャイル開発の原則、パターン、およびテクニック」という本について説明します。 この章はリファクタリングに完全に専念しています。 1つの大きな方法を例として使用して、著者はコードを順次変更し、同時に特定の変更を行う理由を説明します。 各ステップの後、コードはテストを実行します。
明らかに、本からの多くの例はしばしば合成であり、記事の考えを明確にすることのみを目的としています。 したがって、多くの場合、書籍には構文エラーと論理エラーの両方があり、通常、これは本の認識を損ないません。
この記事は著者の信用を落とすという目標を追求するものではなく、彼らの意見を述べ、この問題に関するコミュニティの意見を聞くことは興味深いように思われます。
始めましょう。 著者が変更した元のメソッド:/// <remark> /// , /// . /// . /// /// , 276 . . ., , -- /// 194 . . ., . . /// /// . . /// /// . , /// 2. 2. /// . , /// . /// /// . 9.12.1999 Java /// C# 12.01.2005. ///</remark> using System; /// <summary> /// : . /// </summary> public class GeneratePrimes { ///<summary> /// . ///</summary> /// /// <param name=”maxValue”> .</param> public static int[] GeneratePrimeNumbers(int maxValue) { if (maxValue >= 2) // { // int s = maxValue + 1; // bool[] f = new bool[s]; int i; // true. for (i = 0; i < s; i++) f[i] = true; // f[0] = f[1] = false; // int j; for (i = 2; i < Math.Sqrt(s) + 1; i++) { if (f[i]) // i , . { for (j = 2 * i; j < s; j += i) f[j] = false; // – } } // ? int count = 0; for (i = 0; i < s; i++) { if (f[i]) count++; // } int[] primes = new int[count]; // for (i = 0, j = 0; i < s; i++) { if (f[i]) // primes[j++] = i; } return primes; // } else // maxValue < 2 return new int[0]; // , // } }
記事が終了する最終バージョンですが、著者は理論的には次のように言及しています: ///<remark> /// , /// . /// . /// , 2: /// /// . , . ///</remark> using System; public class PrimeGenerator { private static bool[] crossedOut; private static int[] result; public static int[] GeneratePrimeNumbers(int maxValue) { if (maxValue < 2) return new int[0]; else { UncrossIntegersUpTo(maxValue); CrossOutMultiples(); PutUncrossedIntegersIntoResult(); return result; } } private static void UncrossIntegersUpTo(int maxValue) { crossedOut = new bool[maxValue + 1]; for (int i = 2; i < crossedOut.Length; i++) crossedOut[i] = false; } private static void PutUncrossedIntegersIntoResult() { result = new int[NumberOfUncrossedIntegers()]; for (int j = 0, i = 2; i < crossedOut.Length; i++) { if (NotCrossed(i)) result[j++] = i; } } private static int NumberOfUncrossedIntegers() { int count = 0; for (int i = 2; i < crossedOut.Length; i++) { if (NotCrossed(i)) count++; // } return count; } private static void CrossOutMultiples() { int limit = DetermineIterationLimit(); for (int i = 2; i <= limit; i++) { if (NotCrossed(i)) CrossOutputMultiplesOf(i); } } private static int DetermineIterationLimit() { // // , // , , // . double iterationLimit = Math.Sqrt(crossedOut.Length); return (int)iterationLimit; } private static void CrossOutputMultiplesOf(int i) { for (int multiple = 2 * i; multiple < crossedOut.Length; multiple += i) crossedOut[multiple] = true; } private static bool NotCrossed(int i) { return crossedOut[i] == false; } }
迅速な分析
私の意見では、最初の例では、リファクタリングはまったく必要ありません。不要なコメントを削除するだけで十分ですが、章は面白くありません(笑)。 最後のバージョンでは、パラメーターを受け入れたり返したりしない連続したメソッドによってのみ混乱しています。 私の意見では、しがみつくことは何もないので、そのようなチェーンのロジックを理解することはより困難です。
詳細分析
したがって、最初に目にするのは、メソッドが純粋な関数であり、副作用がないことです。 これにより、その決定性が決まります。同じ入力パラメーターで、同じ出力パラメーターが得られます。 変更後、著者はメソッドからローカル変数をクラスに削除し、それによりクラスに状態を追加し、メソッドに副作用を追加しました。 なぜこれが悪いのですか? 悪い点は、メソッドがスレッドセーフになる前と、その後スレッドセーフになることです。 作成者(または私たち)がメソッドに潜在的にバグを追加しましたが、マルチスレッド環境で使用されていれば、リファクタリングされました。
第二に、変数はまだメソッドに対してローカルであり、クラスの状態を反映しないため、将来、メソッドまたは状態を追加するときに、これらの変数が誰であり、なぜ必要なのかを理解するのがより難しくなります。
結論の代わりに
リファクタリングは素晴らしいです! しかし、多くの場合、卵と鶏肉の問題に直面しています。コードがどのように機能するかを理解するには、リファクタリングする必要があり、リファクタリングするには、その機能を理解する必要があります。
ご清聴ありがとうございました。コメントをお待ちしています。