التجريد في البرمجة

السلام عليكم ورحمة الله

التجريد (abstraction) في عالم هندسة البرمجيات من المواضيع المهمة، حيث يحقق عدة أهداف سوف نتحدث عنها لاحقاً بإذن الله في هذه المقالة. لكن قبل أن نتكلم عنها من ناحية برمجية دعونا نشرحها كمفهوم عام للتجريد في حياتنا اليومية.

التجريد هو التبسيط، وهو عكس التعقيد، وهو تقسيم المشكلة أو التصميم، أو ألأشياء إلى عدة مكونات أكثر بساطة وبدائية. مثلاً جهاز الحاسوب إذا كان قطعة واحدة مصمتة فهو شيء معقد، إذا تلف منه جزء لابد أن يتم استبداله كاملاً، أما التجريد فهو يعني أن يتم فصل أجزاء مختلفة لتشكل مكونات مستقلة يتم تركيبها ووصلها مع بعضها بطريقة إتصال معينة لتعمل في النهاية كأنها جهاز واحد. وكمقارنة بين جهاز حاسوب أكثر تجريداً وواحد آخر أقل تجريداً: نقارن جهاز سطح المكتب بالجهاز المحمول (لابتوب) حيث أن اﻷول (جهاز سطح المكتب) يتكون من أجزاء منفصلة سهل فكها وتغييرها، مثل لوحة المفاتيح، الفأرة، والشاشة، ووحدة المعالجة المركزية، والسماعات. فإذا تلف أي جزء منها، نستطيع شراء بديل له بسهولة، حيث يمكنك شراء أي لوحة مفاتيح من أي نوع، كذلك يمكن ترقية الشاشة لشاشة ذات مساحة عرض أكبر دون أن نقوم بتغيير الحاسوب كاملاً، كذلك يمكنك إستعارة أحد أجزاء الحاسوب المكتبي لتشغيله في حاسوب آخر. أما اللابتوب فهو أقل تجريداً حيث يميل لأن يكون قطعة واحدة، لذلك لا تستطيع تغيير الشاشة إلى شاشة أكبر، ولا تستطيع استعارة الماوس المثبت في اللابتوب لإستخدامه مع جهاز آخر. لكن على اﻷقل يوجد نوع من التجريد وذلك بإمكانية ترقية الذاكرة، أوالقرص الصلب. أما الموبايل فهو أقل تجريداً من اللابتوب، حيث يميل أكثر ليكون جهاز واحد مصمت لذلك إذا تلف فإنه أصعب من ناحية الصيانة، مثلاً لا يمكن تغيير الذاكرة الداخلية إذا تلفت بسهولة. فنجد أن اﻷجهزة اﻷكثر تجريداً تتمتع بعمر خدمي أكبر، وذلك لسهولة الصيانة والترقية. فالجهاز المكتبي لديه عمر أكبر من اللابتوب والذي لديه عمر أكبر من الموبايل، وذلك بمفهوم التجريد وليس بمفهوم جودة الصناعة، نحن نقارن بين منتجات ذات جودة واحدة  في هذا المثال.

التجريد في البرمجة هو نفس المفهوم الذي شرحناه، حيث يهدف إلى تقسيم الأنظمة الكبيرة إلى برامج أصغر ذات وظائف محددة، فبدلاً من أن يكون النظام هو عبارة عن برنامج واحد به شاشات اﻹستخدام، والتقارير، ويحتوي على البيانات، فإن اﻷفضل تقسيمه إلى طبقات: طبقة تتعامل مع المستخدم، نسميها واجهة المستخدم، وطبقة أخرى لاستقبال طلبات المستخدم وتنفيذها، وطبقة أخرى لتخزين البيانات، ويمكن تثبيت كل طبقة في مخدم منفصل حين يكون عدد المستخدمين كبير. كذلك يمكن تقسيم البرامج إلى وحدات أكثر تخصصية من ناحية الوظائف، فمثلاً نظام حسابات وشيكات ومخازن، كل وظيفة يكون لها برنامج أو وحدة برمجية منفصلة، يمكن تطويرها بطريقة مستقلة عن اﻷجزاء اﻷخرى. يوجد أيضاً نوع من التجريد على مستوى اﻹجراءات وعلى مستوى كتابة الكود، فبدلاً من كتابة إجراء طويل ومعقد يمكننا تقسيم هذا اﻹجراء إلى عدة أجراءات أولية بسيطة، كل واحد منها يهدف إلى تحقيق وظيفة واحدة، يمكن إعادة استخدامها أكثر من مرة.

في هذه المقالة سوف نقوم بعرض مثال للنوع اﻷخير من التجريد، وهو على مستوى اﻹجراءات. هذا اﻹجراء (بلغة جافا) يقوم بقراءة رسائل مستلمة والتي لم يتم معالجتها بعد من قاعدة بيانات ثم يقوم بوضعها في شكل مصفوفة. وفي المثال هذا اﻹجراء لا يحقق التجريد، حيث أن به بعض التعقيد ويقوم بعمل أكثر من وظيفة: 1 – تجهيز الطلب لقاعدة البينات، 2 – ثم إرسال الطلب، 3- ثم قراءة السجلات ووضعها في المصفوفة:

 

public ArrayList<SMSMessage> getNewMessages(String shortCodeText){

  success = false;
  try
  {
    ArrayList<SMSMessage> messages;
    PreparedStatement statement = connection.prepareStatement("select * from inbox\n" +
    "inner join shortcodes on shortcodes.ShortCodeID = inbox.ShortCodeID\n" +
    "inner join smppconnections on smppconnections.CONNECTIONID = inbox.connectionid\n" +
    "where Lower(shortCode) = ? \n" +
    "and isProcessed = 0");
    statement.setString(1, shortCodeText.toLowerCase());
    ResultSet result = statement.executeQuery();
    messages = new ArrayList<>();
    while (result.next()){

      SMSMessage message = new SMSMessage();
      message.connectionID = result.getInt("connectionID");
      message.operatorID = result.getInt("operatorID");
      message.fromMDN = result.getString("fromMDN");
      message.shortMessage = result.getString("ShortMsg");
      message.shortCodeStr = result.getString("ShortCodeStr");
      message.isProcessed = result.getInt("isProcessed");
      message.messageID = result.getInt("id");
      message.shortCodeID = result.getInt("shortCodeID");
      message.msgTime; = result.getTimestamp("msgTime");
      messages.add(message);
    }

    success = true;
    return(messages);

  }
  catch (SQLException ex) {
    lastError = "Error in getNewMessages: " + ex.toString();
    General.writeEvent(lastError, "");
    return(null);
  }

}

وهو يعتبر مثال بسيط، لكن توجد في الحياة العملية إجراءات أكثر تعقيداً صعبة الفهم إذا لم يتم تطبيق مفهوم التجريد فيها.سوف نقوم بعمل إعادة صياغة للكود وذلك بقسيمه إلى جزئين، حيث نقوم بإستخلاص الجزء الخاص بطلب المعلومات وقراءة السجلات ووضعها في مصفوفة في إجراء منفصل، ثم نقوم بنداء الإجراء الجديد من داخل اﻹجراء القديم.
استفدنا من المحرر حيث يوجد أداة لإستخلاص هذا الجزء تلقائياً، فقط نقوم بتظليل الجزء المراد إستخلاصه، ثم نختار refactor/introduce/method، ثم قمنا بإختيار إسم له وهو readInbox وهو إسم مجرد يعني قراءة أي نوع من رسائل الواردة، حيث يمكننا قراءة رسائل جديدة بها أو رسائل قديمة، بخلاف الإجراء القديم والذي لا يمكن إستخدامه إلا لقراءة الرسائل الجديدة، فأصبح الكود كالتالي:

public ArrayList<SMSMessage> getNewMessages(String shortCodeText){

  success = false;
  try
  {
    ArrayList<SMSMessage> messages;
    PreparedStatement statement = connection.prepareStatement("select * from inbox\n" +
    "inner join shortcodes on shortcodes.ShortCodeID = inbox.ShortCodeID\n" +
    "inner join smppconnections on smppconnections.CONNECTIONID = inbox.connectionid\n" +
    "where Lower(shortCode) = ? \n" +
    "and isProcessed = 0");
    statement.setString(1, shortCodeText.toLowerCase());
    messages = readInbox(statement);

    success = true;
    return(messages);

  }
  catch (SQLException ex) {
    lastError = "Error in getNewMessages: " + ex.toString();
    General.writeEvent(lastError, "");
    return(null);
  }

}

واﻷجراء الجديد هو:

private ArrayList<SMSMessage> readInbox(PreparedStatement statement) throws SQLException {
  ArrayList<SMSMessage> messages;
  ResultSet result = statement.executeQuery();
  messages = new ArrayList<>();
  while (result.next()){

    SMSMessage message = new SMSMessage();
    message.connectionID = result.getInt("connectionID");
    message.operatorID = result.getInt("operatorID");
    message.fromMDN = result.getString("fromMDN");
    message.shortMessage = result.getString("ShortMsg");
    message.shortCodeStr = result.getString("ShortCodeStr");
    message.isProcessed = result.getInt("isProcessed");
    message.messageID = result.getInt("id");
    message.shortCodeID = result.getInt("shortCodeID");
    message.msgTime = result.getTimestamp("msgTime");
    messages.add(message);
  }
  return messages;
}

نلاحظ أن الإجراء اﻷول (getNewMessages) أصبح أصغروأبسط، و سهل القراءة . كذلك فإن اﻹجراء الجديد (readInbox) أيضاً سهل القراءة والفهم ويمكن إعادة إستخدامه مع إجراءات أخرى، مثلاً لقراءة الرسائل القديمة، لا نحتاج لتكرار جزئية القراءة. وبذلك نكون قد سهلنا الصيانة، مثلاً لو قمنا بإضافة حقيل جديد في الجدول Inbox ماعلينا إلا إضافة سطر واحد لقراءته في إجراء القراءة، لكن إذا كان هُناك تكرار للكود ولم يكن هُناك تجريد، فإن أي تعديل أو إضافة لحقل، يتبعها تغييرات كثيرة في أي مكان من الكود يوجد فيه تكرار قراءة هذه الحقول والسجلات، وإذا نسينا تعديل جزء من اﻷجزاء المكررة ربما تحدث مشكلة في البرنامج.أهداف هندسة البرمجيات التي يحققها التجريد في هذا المثال هي:

    إمكانية إعادة إستخدام الكود
    سهولة الصيانة لأي جزء من الكود
    سهولة القراءة والفهم ومن ثم سهولة إيجاد المشكلة
    سهولة التعديل واﻹختبار
Advertisements

5 أفكار على ”التجريد في البرمجة

  1. السلام عليكم

    اسأل الله ان يبارك فيك ولك، مقال رائع وضح لي نقاط لم اكن لأستوعبها على الوجه الكامل لولا صياغتك لها بطريقة رائعة وضرب الامثلة المبينة لها.

    شكراً ابو اياس وسلامي لأهلنا في السودان الحبيب.

  2. بارك الله فيك اخي ابو اياس , لقد استفدت منك الكثير وانا متابع لك منذ سنوات وتعلمت الكثير منك ربي يجازيك كل خير

اترك رد

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s