خادم (نمط تصميم)
في هندسة البرمجيات ، يُعرّف نمط الخادم كائنًا يُستخدم لتقديم وظيفة معينة لمجموعة من الفئات دون تعريف تلك الوظيفة في كل منها على حدة. الخادم هو فئة توفر مثيلاتها (أو حتى فئتها فقط) طرقًا تتولى خدمة مطلوبة، بينما تُعتبر الكائنات التي يتعامل معها الخادم كمعاملات .
وصف ومثال بسيط
يُستخدم Servant لتوفير سلوك معين لمجموعة من الفئات. فبدلاً من تعريف هذا السلوك في كل فئة على حدة - أو عندما يتعذر علينا استخراج هذا السلوك من الفئة الأصلية المشتركة - يتم تعريفه مرة واحدة في Servant.
على سبيل المثال: لدينا عدة فئات تمثل أشكالًا هندسية (مستطيل، قطع ناقص، ومثلث). يمكننا رسم هذه الأشكال على لوحة رسم. عندما نحتاج إلى توفير دالة "نقل" لهذه الأشكال، يمكننا تنفيذ هذه الدالة في كل فئة، أو يمكننا تعريف واجهة تُنفذها هذه الفئات، ثم توفير وظيفة "النقل" في فئة خادمة. تُعرَّف الواجهة لضمان احتواء الفئات الخادمة على الدوال اللازمة لتوفير السلوك المطلوب. إذا تابعنا مثالنا، نُعرِّف واجهة "Movable" تُحدد أن كل فئة تُنفذ هذه الواجهة يجب أن تُنفذ الدالتين "getPosition" و"setPosition". تُعيد الدالة الأولى موضع الشكل على لوحة الرسم، بينما تُحدد الثانية موضع الشكل وترسمه على لوحة الرسم. ثم نُعرِّف فئة خادمة "MoveServant"، التي تحتوي على الدالتين "moveTo(Movable movedObject, Position where)" و"moveBy(Movable movedObject, int dx, int dy)". يمكن الآن استخدام فئة الخادمة لنقل أي شكل يُنفذ واجهة Movable. وبالتالي يظهر الكود "المتحرك" في فئة واحدة فقط تحترم قاعدة "فصل الاهتمامات".
طريقتان للتنفيذ
هناك طريقتان لتطبيق نمط التصميم هذا:


- يعرف المستخدم الخادم (وفي هذه الحالة، يلزم معرفة الفئات المُخدَّمة) ويرسل رسائل تتضمن طلبات إلى مثيلات الخادم، مُمرِّرًا الكائنات المُخدَّمة كمعاملات. لا تعرف الفئات المُخدَّمة (الكائنات الهندسية في مثالنا) شيئًا عن الخادم، لكنها تُطبِّق واجهة "IServiced". يستدعي صنف المستخدم ببساطة دالة الخادم ويُمرِّر الكائنات المُخدَّمة كمعاملات. يوضح الشكل 1 هذه الحالة.
- تعرف النسخ المُخدَّمة الخادم، ويرسل المستخدم إليها رسائل تتضمن طلبات (في هذه الحالة، لا يلزم معرفة الخادم). ثم ترسل النسخ المُخدَّمة رسائل إلى نسخ الخادم، طالبةً الخدمة. يوضح الشكل 2 الحالة المعاكسة، حيث لا يعرف المستخدم فئة الخادم ويستدعي الفئات المُخدَّمة مباشرةً. عندئذٍ، تطلب الفئات المُخدَّمة من الخادم نفسه تنفيذ الوظيفة المطلوبة.
مثال
يوضح هذا المثال البسيط بلغة جافا الحالة المذكورة أعلاه. هذا المثال توضيحي فقط، ولن يقدم أي رسم فعلي للأشكال الهندسية، ولا وصفًا لشكلها.
// فئة الخادم، التي توفر وظائفها للفئات التي تُنفذ // واجهة Movable public class MoveServant { // دالة تنقل الفئة التي تُنفذ واجهة Movable إلى الموضع حيث public void moveTo ( Movable serviced , Position where ) { // قم ببعض العمليات الأخرى لضمان انتقالها بسلاسة، هذا هو // المكان المناسب لتوفير هذه الوظيفة serviced . setPosition ( where ); }// دالة لتحريك الكائن Movable المُنفذ للفئة بمقدار dx و dy public void moveBy ( Movable serviced , int dx , int dy ) { // هنا يتم تنفيذ هذه الوظيفة dx += serviced . getPosition (). xPosition ; dy += serviced . getPosition (). yPosition ; serviced . setPosition ( new Position ( dx , dy )); } }// واجهة تحدد ما يجب على الفئات المُخدَّمة تنفيذه ليتم خدمتها بواسطة الخادم. public interface Movable { public void setPosition ( Position p );public Position getPosition (); }// إحدى الفئات الهندسية العامة فئة المثلث التي تنفذ واجهة Movable { // موضع الكائن الهندسي على لوحة الرسم الخاصة الموضع p ;// دالة تُحدد موضع الكائن الهندسي public void setPosition ( Position p ) { this . p = p ; }// دالة تُعيد موضع الكائن الهندسي public Position getPosition () { return this . p ; } }// إحدى الفئات الهندسية العامة فئة القطع الناقص تطبق واجهة Movable { // موضع الكائن الهندسي على لوحة الرسم الخاصة الموضع p ;// دالة تُحدد موضع الكائن الهندسي public void setPosition ( Position p ) { this . p = p ; }// دالة تُعيد موضع الكائن الهندسي public Position getPosition () { return this . p ; } }// إحدى الفئات الهندسية public class Rectangle implements Movable { // موضع الكائن الهندسي على لوحة الرسم private Position p ;// دالة تُحدد موضع الكائن الهندسي public void setPosition ( Position p ) { this . p = p ; }// دالة تُعيد موضع الكائن الهندسي public Position getPosition () { return this . p ; } }// مجرد فئة حاوية بسيطة جدًا للموقع. public class Position { public int xPosition ; public int yPosition ;public Position ( int dx , int dy ) { xPosition = dx ; yPosition = dy ; } }نمط تصميم مشابه: الأمر
نمطا التصميم Command وServant متشابهان للغاية، وغالبًا ما تكون تطبيقاتهما متطابقة تقريبًا. ويكمن الاختلاف بينهما في طريقة معالجة المشكلة.
- في نمط الخادم، لدينا بعض الكائنات التي نرغب في تزويدها بوظائف معينة. نقوم بإنشاء فئة تُقدّم مثيلاتها تلك الوظائف، وتُعرّف واجهة يجب على الكائنات المُخدّمة تطبيقها. ثم تُمرّر مثيلات الكائنات المُخدّمة كمعاملات إلى الخادم.
- في نمط الأوامر، لدينا بعض الكائنات التي نرغب في تعديلها بإضافة وظائف معينة. لذلك، نُعرّف واجهةً تُحدد الأوامر التي يجب تنفيذ الوظائف المطلوبة فيها. ثم تُمرر نسخ هذه الأوامر إلى الكائنات الأصلية كمعاملات لأساليبها.
على الرغم من تشابه نمطي التصميم Command وServant، إلا أن هذا لا يعني بالضرورة تطابقهما التام. فهناك العديد من الحالات التي لا يرتبط فيها استخدام نمط التصميم Command بنمط التصميم Servant. في هذه الحالات، نحتاج عادةً إلى تمرير مرجع إلى دالة أخرى إلى الدوال المُستدعاة، وهي الدالة التي تحتاجها لإنجاز مهمتها. ولأننا لا نستطيع تمرير مراجع إلى الدوال في العديد من لغات البرمجة، يتعين علينا تمرير كائن يُنفذ واجهة تُحدد توقيع الدالة المُمررة.
انظر أيضاً
مراجع
موارد
بيتشينوفسكي، رودولف؛ جارميلا بافليتشكوفا؛ لوبوش بافليتشك (يونيو 2006). دعونا نعدل نهج الكائنات أولاً إلى أنماط التصميم أولاً (PDF) . المؤتمر السنوي الحادي عشر للإبتكار والتكنولوجيا في تعليم علوم الحاسوب جامعة بولونيا .
- أنماط تصميم البرمجيات
