استبدال الخوارزمية (Substitute Algorithm)

من موسوعة حسوب

المشكلة

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

الحل

تعديل محتوى التابع (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. التخلُّص من الخوارزميّة القديمة إن اجتازت الخوارزميّةُ الجديدة كافّة الاختبارات بنجاح.

انظر أيضًا

مصادر