الفرق بين المراجعتين ل"Refactoring/substitute algorithm"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(إنشاء الصفحة. هذه الصفحة من مساهمات "نور تامر".)
 
ط (مراجعة وتدقيق.)
 
سطر 9: سطر 9:
  
 
==== قبل إعادة التصميم ====
 
==== قبل إعادة التصميم ====
تتلخَّص مهمة التابع <code>foundPerson</code> بالبحث عن الأشخاص ذوي الأسماء "Don" أو "John" أو "Kent" وذلك بالمرور بعناصر المصفوفة النصّيّة <code>people</code> باستخدام حلقة <code>for</code> كما في الشيفرة:<syntaxhighlight lang="java">
+
تتلخَّص مهمة التابع <code>foundPerson</code> بالبحث عن الأشخاص ذوي الأسماء "Don" أو "John" أو "Kent" وذلك بالمرور بعناصر المصفوفة النصّيّة <code>people</code> باستخدام حلقة <code>for</code> كما في الشيفرة:
 +
 
 +
في لغة Java:<syntaxhighlight lang="java">
 
String foundPerson(String[] people){
 
String foundPerson(String[] people){
 
   for (int i = 0; i < people.length; i++) {
 
   for (int i = 0; i < people.length; i++) {
سطر 25: سطر 27:
 
}
 
}
  
 +
</syntaxhighlight>في لغة #C:<syntaxhighlight lang="c#">
 +
string FoundPerson(string[] people)
 +
{
 +
  for (int i = 0; i < people.Length; i++)
 +
  {
 +
    if (people[i].Equals("Don"))
 +
    {
 +
      return "Don";
 +
    }
 +
    if (people[i].Equals("John"))
 +
    {
 +
      return "John";
 +
    }
 +
    if (people[i].Equals("Kent"))
 +
    {
 +
      return "Kent";
 +
    }
 +
  }
 +
  return String.Empty;
 +
}
 +
</syntaxhighlight>في لغة PHP:<syntaxhighlight lang="php">
 +
function foundPerson(array $people){
 +
  for ($i = 0; $i < count($people); $i++) {
 +
    if ($people[$i] === "Don") {
 +
      return "Don";
 +
    }
 +
    if ($people[$i] === "John") {
 +
      return "John";
 +
    }
 +
    if ($people[$i] === "Kent") {
 +
      return "Kent";
 +
    }
 +
  }
 +
  return "";
 +
}
 +
</syntaxhighlight>في لغة Python:<syntaxhighlight lang="python">
 +
def foundPerson(people):
 +
    for i in range(len(people)):
 +
        if people[i] == "Don":
 +
            return "Don"
 +
        if people[i] == "John":
 +
            return "John"
 +
        if people[i] == "Kent":
 +
            return "Kent"
 +
    return ""
 +
</syntaxhighlight>في لغة TypeScript:<syntaxhighlight lang="typescript">
 +
foundPerson(people: string[]): string{
 +
  for (let person of people) {
 +
    if (person.equals("Don")){
 +
      return "Don";
 +
    }
 +
    if (person.equals("John")){
 +
      return "John";
 +
    }
 +
    if (person.equals("Kent")){
 +
      return "Kent";
 +
    }
 +
  }
 +
  return "";
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
==== بعد إعادة التصميم ====
 +
اختلف محتوى التابع كليًّا وأصبحت خوارزمية البحث تعتمد على قائمةٍ (list) باسم <code>candidates</code> تجري المقارنة مع عناصرها أثناء المرور بعناصر المصفوفة عبر حلقة <code>for</code>، فأصبحت شيفرة التابع بالشكل:
  
==== بعد إعادة التصميم ====
+
في لغة Java:<syntaxhighlight lang="java">
اختلف محتوى التابع كليًّا وأصبحت خوارزمية البحث تعتمد على قائمةٍ (list) باسم <code>candidates</code> تجري المقارنة مع عناصرها أثناء المرور بعناصر المصفوفة عبر حلقة <code>for</code>، فأصبحت شيفرة التابع بالشكل:<syntaxhighlight lang="java">
 
 
String foundPerson(String[] people){
 
String foundPerson(String[] people){
 
   List candidates =
 
   List candidates =
سطر 40: سطر 103:
 
}
 
}
  
 +
</syntaxhighlight>في لغة #C:<syntaxhighlight lang="c#">
 +
string FoundPerson(string[] people)
 +
{
 +
  List<string> candidates = new List<string>() {"Don", "John", "Kent"};
 +
 
 +
  for (int i = 0; i < people.Length; i++)
 +
  {
 +
    if (candidates.Contains(people[i]))
 +
    {
 +
      return people[i];
 +
    }
 +
  }
 +
 
 +
  return String.Empty;
 +
}
 +
</syntaxhighlight>في لغة PHP:<syntaxhighlight lang="php">
 +
function foundPerson(array $people){
 +
  foreach (["Don", "John", "Kent"] as $needle) {
 +
    $id = array_search($needle, $people, true);
 +
    if ($id !== false) {
 +
      return $people[$id];
 +
    }
 +
  }
 +
  return "";
 +
}
 +
</syntaxhighlight>في لغة Python:<syntaxhighlight lang="python">
 +
def foundPerson(people):
 +
    candidates = ["Don", "John", "Kent"]
 +
    for i in range(len(people)):
 +
        if people[i] in candidates:
 +
            return people[i]
 +
    return ""
 +
</syntaxhighlight>في لغة TypeScript:<syntaxhighlight lang="typescript">
 +
foundPerson(people: string[]): string{
 +
  let candidates = ["Don", "John", "Kent"];
 +
  for (let person of people) {
 +
    if (candidates.contains(person)) {
 +
      return person;
 +
    }
 +
  }
 +
  return "";
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
 
== لم إعادة التصميم؟ ==
 
== لم إعادة التصميم؟ ==
 
# ليست إعادة التصميم «التدريجيّة» الطريقةَ الوحيدة لتطوير البرنامج، فقد يكون من الأسهل في بعض الحالات هدمُ جزءٍ من الشيفرة كليًّا والبدء به من جديد، وخاصّة عند العثور على خوارزميةٍ أبسط وأكثر فعاليّةً من الخوارزمية الحاليّة المُستخدَمة.
 
# ليست إعادة التصميم «التدريجيّة» الطريقةَ الوحيدة لتطوير البرنامج، فقد يكون من الأسهل في بعض الحالات هدمُ جزءٍ من الشيفرة كليًّا والبدء به من جديد، وخاصّة عند العثور على خوارزميةٍ أبسط وأكثر فعاليّةً من الخوارزمية الحاليّة المُستخدَمة.
سطر 48: سطر 152:
  
 
== آلية الحل ==
 
== آلية الحل ==
# التأكُّد من تبسيط الخوارزميّة الحاليّة ما أمكن، كنقل الأجزاء غير المهمة منها إلى توابعَ أخرى عبر تقنيّة استخراج التابع (extract method)، فكلمّا ازدادت بساطة الخوارزمية يصبح استبدالها أمرًا أسهل.
+
# التأكُّد من تبسيط الخوارزميّة الحاليّة ما أمكن، كنقل الأجزاء غير المهمة منها إلى توابعَ أخرى عبر تقنيّة [[Refactoring/extract method|استخراج التابع]] (extract method)، فكلمّا ازدادت بساطة الخوارزمية يصبح استبدالها أمرًا أسهل.
 
# كتابة خوارزميّتك البديلة في تابعٍ جديدٍ والاستغناء مؤقتًا عن الخوارزميّة السابقة للبدء باختبار البرنامج (test).
 
# كتابة خوارزميّتك البديلة في تابعٍ جديدٍ والاستغناء مؤقتًا عن الخوارزميّة السابقة للبدء باختبار البرنامج (test).
 
# عند ملاحظة أيّ خللٍ بالنتائج فلا بُدَّ من العودة إلى الخوارزمية السابقة ومقارنة النتائج بين الخوارزميتين لتحديد أسباب الاختلاف، فقد يكون السبب وجودَ خطأٍ ما بالخوارزميّة السابقة أو وجود جزءٍ لا يعمل في الخوارزميّة الجديدة.
 
# عند ملاحظة أيّ خللٍ بالنتائج فلا بُدَّ من العودة إلى الخوارزمية السابقة ومقارنة النتائج بين الخوارزميتين لتحديد أسباب الاختلاف، فقد يكون السبب وجودَ خطأٍ ما بالخوارزميّة السابقة أو وجود جزءٍ لا يعمل في الخوارزميّة الجديدة.
سطر 60: سطر 164:
 
== مصادر ==
 
== مصادر ==
 
* [https://refactoring.guru/substitute-algorithm صفحة توثيق استبدال الخوارزمية في موقع refactoring.guru.]
 
* [https://refactoring.guru/substitute-algorithm صفحة توثيق استبدال الخوارزمية في موقع refactoring.guru.]
[[تصنيف: Refactoring]]
+
[[تصنيف:Refactoring]]
 +
[[تصنيف:Refactoring Techniques]]
 +
[[تصنيف:Refactoring Composing Methods]]

المراجعة الحالية بتاريخ 08:22، 2 مارس 2019

المشكلة

الحاجة إلى استبدال خوارزميّة ما بخوارزميّة أخرى.

الحل

تعديل محتوى التابع (method body) الذي يُنفِّذ الخوارزمية السابقة ليُنفِّذ الخوارزمية الجديدة.

مثال

قبل إعادة التصميم

تتلخَّص مهمة التابع foundPerson بالبحث عن الأشخاص ذوي الأسماء "Don" أو "John" أو "Kent" وذلك بالمرور بعناصر المصفوفة النصّيّة people باستخدام حلقة for كما في الشيفرة:

في لغة Java:

String foundPerson(String[] people){
  for (int i = 0; i < people.length; i++) {
    if (people[i].equals("Don")){
      return "Don";
    }
    if (people[i].equals("John")){
      return "John";
    }
    if (people[i].equals("Kent")){
      return "Kent";
    }
  }
  return "";
}

في لغة #C:

string FoundPerson(string[] people)
{
  for (int i = 0; i < people.Length; i++) 
  {
    if (people[i].Equals("Don"))
    {
      return "Don";
    }
    if (people[i].Equals("John"))
    {
      return "John";
    }
    if (people[i].Equals("Kent"))
    {
      return "Kent";
    }
  }
  return String.Empty;
}

في لغة PHP:

function foundPerson(array $people){
  for ($i = 0; $i < count($people); $i++) {
    if ($people[$i] === "Don") {
      return "Don";
    }
    if ($people[$i] === "John") {
      return "John";
    }
    if ($people[$i] === "Kent") {
      return "Kent";
    }
  }
  return "";
}

في لغة Python:

def foundPerson(people):
    for i in range(len(people)):
        if people[i] == "Don":
            return "Don"
        if people[i] == "John":
            return "John"
        if people[i] == "Kent":
            return "Kent"
    return ""

في لغة TypeScript:

foundPerson(people: string[]): string{
  for (let person of people) {
    if (person.equals("Don")){
      return "Don";
    }
    if (person.equals("John")){
      return "John";
    }
    if (person.equals("Kent")){
      return "Kent";
    }
  }
  return "";
}

بعد إعادة التصميم

اختلف محتوى التابع كليًّا وأصبحت خوارزمية البحث تعتمد على قائمةٍ (list) باسم candidates تجري المقارنة مع عناصرها أثناء المرور بعناصر المصفوفة عبر حلقة for، فأصبحت شيفرة التابع بالشكل:

في لغة Java:

String foundPerson(String[] people){
  List candidates =
    Arrays.asList(new String[] {"Don", "John", "Kent"});
  for (int i=0; i < people.length; i++) {
    if (candidates.contains(people[i])) {
      return people[i];
    }
  }
  return "";
}

في لغة #C:

string FoundPerson(string[] people)
{
  List<string> candidates = new List<string>() {"Don", "John", "Kent"};
  
  for (int i = 0; i < people.Length; i++) 
  {
    if (candidates.Contains(people[i])) 
    {
      return people[i];
    }
  }
  
  return String.Empty;
}

في لغة PHP:

function foundPerson(array $people){
  foreach (["Don", "John", "Kent"] as $needle) {
    $id = array_search($needle, $people, true);
    if ($id !== false) {
      return $people[$id];
    }
  }
  return "";
}

في لغة Python:

def foundPerson(people):
    candidates = ["Don", "John", "Kent"]
    for i in range(len(people)):
        if people[i] in candidates:
            return people[i]
    return ""

في لغة TypeScript:

foundPerson(people: string[]): string{
  let candidates = ["Don", "John", "Kent"];
  for (let person of people) {
    if (candidates.contains(person)) {
      return person;
    }
  }
  return "";
}

لم إعادة التصميم؟

  1. ليست إعادة التصميم «التدريجيّة» الطريقةَ الوحيدة لتطوير البرنامج، فقد يكون من الأسهل في بعض الحالات هدمُ جزءٍ من الشيفرة كليًّا والبدء به من جديد، وخاصّة عند العثور على خوارزميةٍ أبسط وأكثر فعاليّةً من الخوارزمية الحاليّة المُستخدَمة.
  2. قد تتطور المكتبات (libraries) وأُطر العمل (frameworks) مع مرور الزمن لتشمل خوارزميتَك التي أنشأتها، وسيكون من الأفضل حينئذٍ التخلُّص من تنفيذك (implementation) الحاليّ لها والاستفادة مما تحتويه المكتبة إذ سيجعل ذلك من الصيانة (maintenance) أمرًا أسهل.
  3. قد تختلف متطلبات البرنامج كثيرًا إلى أن تصبح الخوارزمية الحاليّة غير مجديةٍ ولا كفيلةٍ بأداء المهمة.

آلية الحل

  1. التأكُّد من تبسيط الخوارزميّة الحاليّة ما أمكن، كنقل الأجزاء غير المهمة منها إلى توابعَ أخرى عبر تقنيّة استخراج التابع (extract method)، فكلمّا ازدادت بساطة الخوارزمية يصبح استبدالها أمرًا أسهل.
  2. كتابة خوارزميّتك البديلة في تابعٍ جديدٍ والاستغناء مؤقتًا عن الخوارزميّة السابقة للبدء باختبار البرنامج (test).
  3. عند ملاحظة أيّ خللٍ بالنتائج فلا بُدَّ من العودة إلى الخوارزمية السابقة ومقارنة النتائج بين الخوارزميتين لتحديد أسباب الاختلاف، فقد يكون السبب وجودَ خطأٍ ما بالخوارزميّة السابقة أو وجود جزءٍ لا يعمل في الخوارزميّة الجديدة.
  4. التخلُّص من الخوارزميّة القديمة إن اجتازت الخوارزميّةُ الجديدة كافّة الاختبارات بنجاح.

انظر أيضًا

مصادر