ConstructiCat Logo
CodeBust.
Browse section ▾

أسلوب القالب.

أسلوب القالب هو نمط تصميم سلوكي يُعرّف الهيكل العام لخوارزمية في الصنف الأساسي، لكنه يتيح للأصناف الفرعية إعادة تعريف خطوات محددة من الخوارزمية دون تغيير بنيتها.

##Intent

أسلوب القالب هو نمط تصميم سلوكي يُعرّف الهيكل العام لخوارزمية في الصنف الأساسي، لكنه يتيح للأصناف الفرعية إعادة تعريف خطوات محددة من الخوارزمية دون تغيير بنيتها.

نمط التصميم أسلوب القالب

##Problem

تخيّل أنك تنشئ تطبيقًا للتنقيب عن البيانات (data mining) يحلّل مستندات الشركات. يُغذّي المستخدمون التطبيق بمستندات بصيغ مختلفة (PDF، DOC، CSV)، ويحاول استخراج بيانات ذات معنى من هذه المستندات بصيغة موحّدة.

كان بإمكان النسخة الأولى من التطبيق العمل مع ملفات DOC فقط. وفي النسخة التالية، أصبح قادرًا على دعم ملفات CSV. وبعد شهر، “علّمته” استخراج البيانات من ملفات PDF.

أصناف التنقيب عن البيانات احتوت على كثير من الشيفرة المكررة

أصناف التنقيب عن البيانات احتوت على كثير من الشيفرة المكررة.

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

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

##Solution

يقترح نمط أسلوب القالب أن تُقسّم الخوارزمية إلى سلسلة من الخطوات، وتحوّل هذه الخطوات إلى أساليب، وتضع سلسلة من الاستدعاءات لهذه الأساليب داخل أسلوب قالب واحد. ويمكن أن تكون الخطوات إمّا abstract (مجردة)، أو أن تمتلك تطبيقًا افتراضيًا ما. ولاستخدام الخوارزمية، يُفترض أن يوفّر العميل صنفه الفرعي الخاص، ويُنفّذ جميع الخطوات المجردة، ويُعيد تعريف بعض الخطوات الاختيارية عند الحاجة (لكن ليس أسلوب القالب نفسه).

لنرَ كيف سيجري ذلك في تطبيق التنقيب عن البيانات لدينا. يمكننا إنشاء صنف أساسي لخوارزميات التحليل الثلاث جميعها. يُعرّف هذا الصنف أسلوب قالب يتكوّن من سلسلة من الاستدعاءات لمختلف خطوات معالجة المستندات.

أسلوب القالب يُعرّف الهيكل العام للخوارزمية

يقسّم أسلوب القالب الخوارزمية إلى خطوات، مما يتيح للأصناف الفرعية إعادة تعريف هذه الخطوات لكن ليس الأسلوب الفعلي.

في البداية، يمكننا إعلان جميع الخطوات على أنها abstract (مجردة)، مما يُجبر الأصناف الفرعية على توفير تطبيقاتها الخاصة لهذه الأساليب. وفي حالتنا، تمتلك الأصناف الفرعية بالفعل جميع التطبيقات اللازمة، لذا فإن الشيء الوحيد الذي قد نحتاج إلى فعله هو تعديل تواقيع الأساليب لتطابق أساليب الصنف الأساسي.

الآن، لنرَ ما يمكننا فعله للتخلص من الشيفرة المكررة. يبدو أن الشيفرة الخاصة بفتح/إغلاق الملفات واستخراج/تحليل البيانات تختلف باختلاف صيغ البيانات، لذا لا فائدة من المساس بتلك الأساليب. ومع ذلك، فإن تطبيق خطوات أخرى، مثل تحليل البيانات الخام وتأليف التقارير، متشابه جدًا، لذا يمكن رفعه إلى الصنف الأساسي حيث يمكن للأصناف الفرعية مشاركة تلك الشيفرة.

كما ترى، لدينا نوعان من الخطوات:

  • الخطوات المجردة يجب أن يُنفّذها كل صنف فرعي
  • الخطوات الاختيارية تمتلك بالفعل تطبيقًا افتراضيًا ما، لكن لا يزال بالإمكان إعادة تعريفها عند الحاجة

هناك نوع آخر من الخطوات يُسمى الخطافات (hooks). الخطّاف هو خطوة اختيارية بجسم فارغ. وسيعمل أسلوب القالب حتى لو لم تُعَد تعريف الخطّاف. وعادةً ما تُوضع الخطافات قبل الخطوات الحاسمة للخوارزميات وبعدها، مما يوفّر للأصناف الفرعية نقاط توسيع إضافية للخوارزمية.

##Structure

بنية نمط التصميم أسلوب القالببنية نمط التصميم أسلوب القالب
  1. يُعلن الصنف المجرد عن أساليب تعمل كخطوات لخوارزمية، إضافةً إلى أسلوب القالب الفعلي الذي يستدعي هذه الأساليب بترتيب محدد. ويمكن أن تكون الخطوات إمّا مُعلَنة على أنها abstract (مجردة) أو أن تمتلك تطبيقًا افتراضيًا ما.

  2. تستطيع الأصناف المحسوسة إعادة تعريف جميع الخطوات، لكن ليس أسلوب القالب نفسه.

##Pseudocode

في هذا المثال، يوفّر نمط أسلوب القالب “هيكلًا عامًا” لمختلف فروع الذكاء الاصطناعي في لعبة فيديو استراتيجية بسيطة.

بنية مثال على نمط أسلوب القالب

أصناف الذكاء الاصطناعي للعبة فيديو بسيطة.

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

// يُعرّف الصنف المجرد أسلوب قالب يحتوي على هيكل عام لخوارزمية
// ما، مكوَّن من استدعاءات، عادةً لعمليات أولية مجردة. تُنفّذ
// الأصناف الفرعية المحسوسة هذه العمليات، لكنها تترك أسلوب
// القالب نفسه كما هو دون
// تغيير.
class GameAI is
    // يُعرّف أسلوب القالب الهيكل العام للخوارزمية.
    method turn() is
        collectResources()
        buildStructures()
        buildUnits()
        attack()

    // يمكن تطبيق بعض الخطوات مباشرةً في الصنف
    // الأساسي.
    method collectResources() is
        foreach (s in this.builtStructures) do
            s.collect()

    // وبعضها يمكن تعريفه على أنه مجرد.
    abstract method buildStructures()
    abstract method buildUnits()

    // يمكن أن يحتوي الصنف على عدة أساليب قالب.
    method attack() is
        enemy = closestEnemy()
        if (enemy == null)
            sendScouts(map.center)
        else
            sendWarriors(enemy.position)

    abstract method sendScouts(position)
    abstract method sendWarriors(position)

// يجب على الأصناف المحسوسة تنفيذ جميع العمليات المجردة
// للصنف الأساسي، لكن يجب ألا تُعيد تعريف أسلوب القالب
// نفسه.
class OrcsAI extends GameAI is
    method buildStructures() is
        if (there are some resources) then
            // ابنِ المزارع، ثم الثكنات، ثم الحصن.

    method buildUnits() is
        if (there are plenty of resources) then
            if (there are no scouts)
                // ابنِ عاملًا (peon) وأضفه إلى مجموعة الكشافة.
            else
                // ابنِ مقاتلًا (grunt) وأضفه إلى مجموعة المحاربين.

    // ...

    method sendScouts(position) is
        if (scouts.length > 0) then
            // أرسل الكشافة إلى الموقع.

    method sendWarriors(position) is
        if (warriors.length > 5) then
            // أرسل المحاربين إلى الموقع.

// يمكن للأصناف الفرعية أيضًا إعادة تعريف بعض العمليات
// بتطبيق افتراضي.
class MonstersAI extends GameAI is
    method collectResources() is
        // الوحوش لا تجمع الموارد.

    method buildStructures() is
        // الوحوش لا تبني مبانٍ.

    method buildUnits() is
        // الوحوش لا تبني وحدات.

##Applicability

استخدم نمط أسلوب القالب عندما تريد أن تتيح للعملاء توسيع خطوات معينة فقط من خوارزمية، دون توسيع الخوارزمية بأكملها أو بنيتها.

يتيح لك أسلوب القالب تحويل خوارزمية متراصة (monolithic) إلى سلسلة من الخطوات المنفردة التي يمكن للأصناف الفرعية توسيعها بسهولة مع الحفاظ على البنية المُعرّفة في الصنف الأساسي.

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

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

##How to Implement

  1. حلّل الخوارزمية المستهدفة لمعرفة ما إذا كان بإمكانك تقسيمها إلى خطوات. فكّر في الخطوات المشتركة بين جميع الأصناف الفرعية وتلك التي ستكون فريدة دائمًا.

  2. أنشئ الصنف الأساسي المجرد وأعلن عن أسلوب القالب ومجموعة من الأساليب المجردة التي تمثّل خطوات الخوارزمية. ارسم مخطط بنية الخوارزمية داخل أسلوب القالب عبر استدعاء الخطوات المقابلة. فكّر في جعل أسلوب القالب final لمنع الأصناف الفرعية من إعادة تعريفه.

  3. لا بأس إن انتهى الأمر بأن تكون جميع الخطوات مجردة. ومع ذلك، قد تستفيد بعض الخطوات من امتلاك تطبيق افتراضي. فالأصناف الفرعية ليست مضطرة لتنفيذ تلك الأساليب.

  4. فكّر في إضافة خطافات (hooks) بين الخطوات الحاسمة للخوارزمية.

  5. لكل صيغة مختلفة من الخوارزمية، أنشئ صنفًا فرعيًا محسوسًا جديدًا. فهو يجب أن يُنفّذ جميع الخطوات المجردة، لكنه قد يُعيد أيضًا تعريف بعض الخطوات الاختيارية.

##Pros & Cons

Pros
  • يمكنك السماح للعملاء بإعادة تعريف أجزاء معينة فقط من خوارزمية كبيرة، مما يجعلهم أقل تأثرًا بالتغييرات التي تطرأ على أجزاء أخرى من الخوارزمية.
  • يمكنك رفع الشيفرة المكررة إلى صنف أساسي.
Cons
  • قد يكون بعض العملاء مُقيَّدين بالهيكل العام المُتاح للخوارزمية.
  • قد تنتهك مبدأ ليسكوف للاستبدال عبر كبت التطبيق الافتراضي لإحدى الخطوات من خلال صنف فرعي.
  • تميل أساليب القالب إلى أن تصبح أصعب في الصيانة كلما زاد عدد خطواتها.

##Relations with Other Patterns

  • أسلوب المصنع هو تخصيص لـأسلوب القالب. وفي الوقت نفسه، قد يكون أسلوب المصنع بمثابة خطوة ضمن أسلوب قالب كبير.

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