الجسر.
الجسر هو نمط تصميم هيكلي يتيح لك تقسيم كلاس ضخم أو مجموعة من الكلاسات المترابطة إلى تسلسلين هرميين منفصلين — التجريد والتنفيذ — يمكن تطويرهما بشكل مستقل عن بعضهما.
##Intent
الجسر هو نمط تصميم هيكلي يتيح لك تقسيم كلاس ضخم أو مجموعة من الكلاسات المترابطة إلى تسلسلين هرميين منفصلين — التجريد والتنفيذ — يمكن تطويرهما بشكل مستقل عن بعضهما.

##Problem
التجريد؟ التنفيذ؟ يبدو الأمر مخيفاً؟ لا تقلق، دعنا نتأمل مثالاً بسيطاً.
لنفترض أن لديك كلاساً هندسياً Shape مع زوج من الكلاسات الفرعية: Circle وSquare. تريد توسيع هذا التسلسل الهرمي ليشمل الألوان، لذا تخطط لإنشاء كلاسات فرعية Red وBlue. غير أنك بما أن لديك كلاسين فرعيين بالفعل، ستحتاج إلى إنشاء أربع تركيبات مثل BlueCircle وRedSquare.

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

يمكنك منع الانفجار في التسلسل الهرمي للكلاسات بتحويله إلى عدة تسلسلات هرمية مترابطة.
باتباع هذا النهج، يمكننا استخراج الكود المتعلق بالألوان في كلاسه الخاص مع كلاسين فرعيين: Red وBlue. يحصل كلاس Shape بعد ذلك على حقل مرجع يشير إلى أحد كائنات الألوان. يمكن للشكل الآن تفويض أي عمل متعلق بالألوان إلى كائن اللون المرتبط به. سيعمل ذلك المرجع كجسر بين كلاسَي Shape وColor. من الآن فصاعداً، إضافة ألوان جديدة لن تستلزم تغيير التسلسل الهرمي للأشكال، والعكس صحيح.
التجريد والتنفيذ
يُقدِّم كتاب GoF مصطلحَي التجريد والتنفيذ كجزء من تعريف نمط الجسر. في رأيي، يبدو هذان المصطلحان أكاديميَّين للغاية ويجعلان النمط يبدو أكثر تعقيداً مما هو عليه فعلاً. بعد قراءة المثال البسيط عن الأشكال والألوان، دعنا نفك شفرة الكلمات المخيفة في كتاب GoF.
التجريد (يُسمى أيضاً الواجهة) هو طبقة تحكم عالية المستوى لكيان ما. لا يُفترض أن تُنجز هذه الطبقة أي عمل حقيقي بنفسها، بل يجب أن تُفوِّض العمل إلى طبقة التنفيذ (يُسمى أيضاً المنصة).
لاحظ أننا لا نتحدث عن الواجهات أو الكلاسات التجريدية من لغة البرمجة الخاصة بك. هذه ليست الشيء نفسه.
عند الحديث عن التطبيقات الحقيقية، يمكن تمثيل التجريد بواجهة المستخدم الرسومية (GUI)، وقد يكون التنفيذ هو كود نظام التشغيل الأساسي (API) الذي تستدعيه طبقة واجهة المستخدم الرسومية استجابةً لتفاعلات المستخدم.
بشكل عام، يمكنك توسيع مثل هذا التطبيق في اتجاهين مستقلين:
- امتلاك عدة واجهات مستخدم رسومية مختلفة (على سبيل المثال، مُصمَّمة للعملاء العاديين أو المسؤولين).
- دعم عدة واجهات برمجة تطبيقات مختلفة (على سبيل المثال، للتمكن من تشغيل التطبيق على Windows وLinux وmacOS).
في أسوأ سيناريو ممكن، قد يبدو هذا التطبيق وعاءً هائلاً من السباغيتي، حيث تربط مئات الشروط أنواعاً مختلفة من واجهات المستخدم الرسومية بمختلف واجهات برمجة التطبيقات في أرجاء الكود.

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

أحد الطرق لهيكلة تطبيق متعدد المنصات.
يتحكم كائن التجريد في مظهر التطبيق، مُفوِّضاً العمل الفعلي إلى كائن التنفيذ المرتبط به. التنفيذات المختلفة قابلة للتبادل طالما أنها تتبع واجهة مشتركة، مما يتيح لنفس واجهة المستخدم الرسومية العمل على Windows وLinux.
نتيجةً لذلك، يمكنك تغيير كلاسات واجهة المستخدم الرسومية دون المساس بالكلاسات المتعلقة بواجهة برمجة التطبيقات. علاوة على ذلك، إضافة دعم لنظام تشغيل آخر تستلزم فقط إنشاء كلاس فرعي في تسلسل التنفيذ الهرمي.
##Structure
-
يوفر التجريد منطق التحكم عالي المستوى. ويعتمد على كائن التنفيذ لأداء العمل الفعلي على المستوى المنخفض.
-
يُعلن التنفيذ عن الواجهة المشتركة لجميع التنفيذات الملموسة. يمكن للتجريد التواصل مع كائن التنفيذ فقط عبر الأساليب المُعلَن عنها هنا.
قد يُدرج التجريد نفس أساليب التنفيذ، لكنه عادةً يُعلن عن سلوكيات معقدة تعتمد على مجموعة واسعة من العمليات الأساسية المُعلَن عنها في التنفيذ.
-
تحتوي التنفيذات الملموسة على كود خاص بالمنصة.
-
توفر التجريدات المُحسَّنة متغيرات من منطق التحكم. وكوالديها، تعمل مع تنفيذات مختلفة عبر واجهة التنفيذ العامة.
-
عادةً يهتم العميل فقط بالعمل مع التجريد. غير أن مهمة العميل هي ربط كائن التجريد بأحد كائنات التنفيذ.
##Pseudocode
يوضح هذا المثال كيف يمكن لنمط الجسر تقسيم الكود الضخم لتطبيق يدير الأجهزة وأجهزة التحكم عن بُعد الخاصة بها. تعمل كلاسات Device كتنفيذ، في حين تعمل كلاسات Remote كتجريد.

يُقسَّم التسلسل الهرمي الأصلي للكلاسات إلى جزأين: الأجهزة وأجهزة التحكم عن بُعد.
يُعلن كلاس جهاز التحكم الأساسي عن حقل مرجع يربطه بكائن الجهاز. تعمل جميع أجهزة التحكم مع الأجهزة عبر واجهة الجهاز العامة، مما يتيح لجهاز التحكم ذاته دعم أنواع متعددة من الأجهزة.
يمكنك تطوير كلاسات أجهزة التحكم بشكل مستقل عن كلاسات الأجهزة. كل ما تحتاجه هو إنشاء كلاس فرعي جديد لجهاز التحكم. على سبيل المثال، قد يحتوي جهاز التحكم الأساسي على زرين فقط، لكن يمكنك توسيعه بميزات إضافية، كبطارية إضافية أو شاشة تعمل باللمس.
يربط كود العميل النوع المطلوب من جهاز التحكم بكائن جهاز محدد عبر مُنشئ جهاز التحكم.
// يُعرِّف "التجريد" الواجهةَ للجزء "التحكمي"
// من التسلسلين الهرميين للكلاسات. ويحتفظ بمرجع
// لكائن من تسلسل "التنفيذ" الهرمي ويُفوِّض
// كل العمل الفعلي لهذا الكائن.
class RemoteControl is
protected field device: Device
constructor RemoteControl(device: Device) is
this.device = device
method togglePower() is
if (device.isEnabled()) then
device.disable()
else
device.enable()
method volumeDown() is
device.setVolume(device.getVolume() - 10)
method volumeUp() is
device.setVolume(device.getVolume() + 10)
method channelDown() is
device.setChannel(device.getChannel() - 1)
method channelUp() is
device.setChannel(device.getChannel() + 1)
// يمكنك توسيع الكلاسات من تسلسل التجريد الهرمي
// بشكل مستقل عن كلاسات الأجهزة.
class AdvancedRemoteControl extends RemoteControl is
method mute() is
device.setVolume(0)
// تُعلن واجهة "التنفيذ" عن أساليب مشتركة لجميع
// كلاسات التنفيذ الملموسة. ولا يلزم أن تتطابق مع
// واجهة التجريد. في الواقع، قد تكون الواجهتان مختلفتين تماماً.
// عادةً توفر واجهة التنفيذ عمليات أساسية فقط، بينما
// يُعرِّف التجريد عمليات أعلى مستوى بناءً على تلك العمليات الأساسية.
interface Device is
method isEnabled()
method enable()
method disable()
method getVolume()
method setVolume(percent)
method getChannel()
method setChannel(channel)
// جميع الأجهزة تتبع نفس الواجهة.
class Tv implements Device is
// ...
class Radio implements Device is
// ...
// في مكان ما في كود العميل.
tv = new Tv()
remote = new RemoteControl(tv)
remote.togglePower()
radio = new Radio()
remote = new AdvancedRemoteControl(radio)
##Applicability
استخدم نمط الجسر عندما تريد تقسيم وتنظيم كلاس ضخم يحتوي على عدة متغيرات من وظيفة معينة (على سبيل المثال، إذا كان الكلاس يمكنه العمل مع خوادم قواعد بيانات متعددة).
كلما كبر الكلاس، كلما صعُب فهم آلية عمله وزاد الوقت اللازم لإجراء أي تغيير. إذ قد تستلزم التغييرات على أحد متغيرات الوظيفة إجراء تعديلات على الكلاس بأكمله، مما يؤدي في الغالب إلى وقوع أخطاء أو إغفال بعض الآثار الجانبية الحرجة.
يتيح لك نمط الجسر تقسيم الكلاس الضخم إلى عدة تسلسلات هرمية للكلاسات. وبعد ذلك يمكنك تعديل الكلاسات في كل تسلسل هرمي بشكل مستقل عن الكلاسات في التسلسلات الأخرى. يُبسِّط هذا النهج صيانة الكود ويُقلِّل من خطر كسر الكود الموجود.
استخدم النمط عندما تحتاج إلى توسيع كلاس في عدة أبعاد متعامدة (مستقلة).
يقترح الجسر استخراج تسلسل هرمي منفصل لكل بُعد من الأبعاد. يُفوِّض الكلاس الأصلي العمل المرتبط بتلك الأبعاد إلى الكائنات المنتمية إلى تلك التسلسلات الهرمية بدلاً من القيام بكل شيء بنفسه.
استخدم الجسر إذا احتجت إلى تبديل التنفيذات في وقت التشغيل.
على الرغم من كونه اختيارياً، يتيح لك نمط الجسر استبدال كائن التنفيذ داخل التجريد. الأمر بسيط كتعيين قيمة جديدة لحقل.
بالمناسبة، هذا البند الأخير هو السبب الرئيسي وراء خلط كثير من المطورين بين نمط الجسر ونمط الاستراتيجية. تذكر أن النمط أكثر من مجرد طريقة معينة لهيكلة كلاساتك؛ فهو أيضاً يعبِّر عن القصد والمشكلة التي يعالجها.
##How to Implement
-
حدِّد الأبعاد المتعامدة في كلاساتك. هذه المفاهيم المستقلة يمكن أن تكون: التجريد/المنصة، والمجال/البنية التحتية، والواجهة الأمامية/الخلفية، أو الواجهة/التنفيذ.
-
تعرَّف على العمليات التي يحتاجها العميل وعرِّفها في كلاس التجريد الأساسي.
-
حدِّد العمليات المتاحة على جميع المنصات. أعلِن عن العمليات التي يحتاجها التجريد في واجهة التنفيذ العامة.
-
لجميع المنصات في مجالك، أنشئ كلاسات تنفيذ ملموسة، لكن تأكد من أنها جميعها تتبع واجهة التنفيذ.
-
داخل كلاس التجريد، أضف حقل مرجع لنوع التنفيذ. يُفوِّض التجريد معظم العمل إلى كائن التنفيذ المُشار إليه في ذلك الحقل.
-
إذا كان لديك عدة متغيرات من المنطق عالي المستوى، فأنشئ تجريدات مُحسَّنة لكل متغير بتوسيع كلاس التجريد الأساسي.
-
يجب أن يمرر كود العميل كائن تنفيذ إلى مُنشئ التجريد لربطهما ببعض. بعد ذلك يمكن للعميل نسيان أمر التنفيذ والعمل فقط مع كائن التجريد.
##Pros & Cons
- يمكنك إنشاء كلاسات وتطبيقات مستقلة عن المنصة.
- يتعامل كود العميل مع تجريدات عالية المستوى ولا يتعرض لتفاصيل المنصة.
- مبدأ مفتوح/مغلق. يمكنك إدخال تجريدات وتنفيذات جديدة بشكل مستقل عن بعضها.
- مبدأ المسؤولية الواحدة. يمكنك التركيز على المنطق عالي المستوى في التجريد، وعلى تفاصيل المنصة في التنفيذ.
- قد تجعل الكود أكثر تعقيداً عند تطبيق النمط على كلاس متماسك للغاية.
##Relations with Other Patterns
-
عادةً يُصمَّم الجسر مسبقاً، مما يتيح لك تطوير أجزاء التطبيق بشكل مستقل عن بعضها. في المقابل، يُستخدم المحوِّل عادةً مع تطبيق قائم لجعل بعض الكلاسات غير المتوافقة تعمل معاً بشكل جيد.
-
يمتلك الجسر والحالة والاستراتيجية (وإلى حد ما المحوِّل) هياكل متشابهة جداً. في الواقع، تستند جميع هذه الأنماط إلى التركيب، وهو تفويض العمل إلى كائنات أخرى. غير أنها جميعاً تحل مشاكل مختلفة. النمط ليس مجرد وصفة لهيكلة الكود بطريقة معينة؛ فهو أيضاً يُوصل إلى المطورين الآخرين المشكلة التي يحلها.
-
يمكنك استخدام المصنع التجريدي جنباً إلى جنب مع الجسر. هذا الترادف مفيد عندما يمكن لبعض التجريدات المُعرَّفة بواسطة الجسر أن تعمل فقط مع تنفيذات محددة. في هذه الحالة، يمكن للمصنع التجريدي تغليف هذه العلاقات وإخفاء التعقيد عن كود العميل.
-
يمكنك دمج المنشئ مع الجسر: يؤدي كلاس المخرج دور التجريد، بينما تعمل المنشئات المختلفة كتنفيذات.