المركَّب.
المركَّب هو نمط تصميم هيكلي يتيح لك تكوين الكائنات في هياكل شجرية ثم العمل مع هذه الهياكل كما لو كانت كائنات فردية.
##Intent
المركَّب هو نمط تصميم هيكلي يتيح لك تكوين الكائنات في هياكل شجرية ثم العمل مع هذه الهياكل كما لو كانت كائنات فردية.

##Problem
استخدام نمط المركَّب منطقي فقط عندما يمكن تمثيل النموذج الأساسي لتطبيقك كهيكل شجري.
على سبيل المثال، تخيّل أن لديك نوعين من الكائنات: Products وBoxes. يمكن أن تحتوي الـBox على عدة Products فضلاً عن عدد من Boxes الأصغر. يمكن لهذه الصناديق الصغيرة بدورها أن تحتوي على بعض Products أو حتى Boxes أصغر، وهكذا دواليك.
لنفترض أنك قررت إنشاء نظام طلبات يستخدم هذه الفئات. يمكن أن تحتوي الطلبات على منتجات بسيطة بدون أي تغليف، وكذلك صناديق محشوة بمنتجات... وصناديق أخرى. كيف ستحدد السعر الإجمالي لمثل هذا الطلب؟

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

يتيح لك نمط المركَّب تشغيل سلوك بشكل متكرر على جميع مكوّنات شجرة الكائنات.
أكبر فائدة من هذا النهج هي أنك لست بحاجة إلى الاهتمام بالفئات المحددة للكائنات التي تُشكّل الشجرة. لا تحتاج إلى معرفة ما إذا كان الكائن منتجاً بسيطاً أم صندوقاً معقداً. يمكنك التعامل معها جميعاً بنفس الطريقة من خلال الواجهة المشتركة. عند استدعاء طريقة ما، تمرر الكائنات نفسها الطلب أسفل الشجرة.
##Structure
-
تصف واجهة المكوّن العمليات المشتركة لكلٍّ من العناصر البسيطة والمعقدة في الشجرة.
-
الورقة هي العنصر الأساسي في الشجرة الذي لا يملك عناصر فرعية.
عادةً ما تنتهي مكوّنات الورقة بالقيام بمعظم العمل الحقيقي، لأنه ليس لديها من تفوّض العمل إليه.
-
الحاوية (المعروفة أيضاً بـالمركَّب) هي عنصر يملك عناصر فرعية: أوراق أو حاويات أخرى. لا تعرف الحاوية الفئات المحددة لأبنائها. إنها تعمل مع جميع العناصر الفرعية فقط عبر واجهة المكوّن.
عند تلقّي طلب، تفوّض الحاوية العمل إلى عناصرها الفرعية، وتعالج النتائج الوسيطة ثم تعيد النتيجة النهائية إلى العميل.
-
يتعامل العميل مع جميع العناصر عبر واجهة المكوّن. ونتيجةً لذلك، يمكن للعميل العمل بنفس الطريقة مع كلٍّ من العناصر البسيطة والمعقدة في الشجرة.
##Pseudocode
في هذا المثال، يتيح لك نمط المركَّب تنفيذ تكديس الأشكال الهندسية في محرر رسومي.

مثال محرر الأشكال الهندسية.
فئة CompoundGraphic هي حاوية يمكنها أن تضم أي عدد من الأشكال الفرعية، بما فيها أشكال مركّبة أخرى. تمتلك الشكل المركّب نفس الطرق التي يمتلكها الشكل البسيط. ومع ذلك، بدلاً من القيام بشيء بنفسه، يمرر الشكل المركّب الطلب بشكل متكرر إلى جميع أبنائه ثم "يجمع" النتيجة.
يعمل كود العميل مع جميع الأشكال من خلال الواجهة الواحدة المشتركة بين جميع فئات الأشكال. وبذلك، لا يعرف العميل ما إذا كان يعمل مع شكل بسيط أم مركّب. يمكن للعميل العمل مع هياكل كائنات بالغة التعقيد دون الارتباط بالفئات المحددة التي تُشكّل تلك البنية.
// تُعلن واجهة المكوّن عن العمليات المشتركة لكلٍّ من
// الكائنات البسيطة والمعقدة في التركيب.
interface Graphic is
method move(x, y)
method draw()
// تمثّل فئة الورقة الكائنات الطرفية في التركيب. لا يمكن
// لكائن الورقة أن يمتلك كائنات فرعية. عادةً ما تقوم كائنات
// الورقة بالعمل الفعلي، في حين تفوّض كائنات المركَّب فقط
// إلى مكوّناتها الفرعية.
class Dot implements Graphic is
field x, y
constructor Dot(x, y) { ... }
method move(x, y) is
this.x += x, this.y += y
method draw() is
// ارسم نقطة عند X وY.
// يمكن لجميع فئات المكوّن أن تمتد من مكوّنات أخرى.
class Circle extends Dot is
field radius
constructor Circle(x, y, radius) { ... }
method draw() is
// ارسم دائرة عند X وY بنصف قطر R.
// تمثّل فئة المركَّب المكوّنات المعقدة التي قد تمتلك أبناء.
// تفوّض كائنات المركَّب عادةً العمل الفعلي لأبنائها ثم "تجمع" النتيجة.
class CompoundGraphic implements Graphic is
field children: array of Graphic
// يمكن لكائن المركَّب إضافة أو إزالة مكوّنات أخرى
// (بسيطة أو معقدة) من قائمة أبنائه.
method add(child: Graphic) is
// أضف ابناً إلى مصفوفة الأبناء.
method remove(child: Graphic) is
// أزل ابناً من مصفوفة الأبناء.
method move(x, y) is
foreach (child in children) do
child.move(x, y)
// يُنفّذ المركَّب منطقه الأساسي بطريقة معينة. يتجول بشكل
// متكرر عبر جميع أبنائه، جامعاً نتائجهم. وبما أن أبناء
// المركَّب يمررون هذه الاستدعاءات لأبنائهم وهكذا، فإن
// شجرة الكائنات بأكملها تُعبَر نتيجةً لذلك.
method draw() is
// 1. لكل مكوّن فرعي:
// - ارسم المكوّن.
// - حدّث المستطيل المحيط.
// 2. ارسم مستطيلاً منقّطاً باستخدام
// إحداثيات الحدود.
// يتعامل كود العميل مع جميع المكوّنات عبر واجهتها الأساسية.
// وبهذه الطريقة يمكن لكود العميل دعم مكوّنات الورقة البسيطة
// وكذلك المركّبات المعقدة.
class ImageEditor is
field all: CompoundGraphic
method load() is
all = new CompoundGraphic()
all.add(new Dot(1, 2))
all.add(new Circle(5, 3, 10))
// ...
// ادمج المكوّنات المحددة في مكوّن مركّب معقد واحد.
method groupSelected(components: array of Graphic) is
group = new CompoundGraphic()
foreach (component in components) do
group.add(component)
all.remove(component)
all.add(group)
// سيتم رسم جميع المكوّنات.
all.draw()
##Applicability
استخدم نمط المركَّب عندما تضطر إلى تنفيذ هيكل كائنات يشبه الشجرة.
يوفر لك نمط المركَّب نوعين أساسيين من العناصر تشتركان في واجهة مشتركة: أوراق بسيطة وحاويات معقدة. يمكن أن تتكوّن الحاوية من أوراق وحاويات أخرى. يتيح لك هذا بناء هيكل كائنات متداخل متكرر يشبه الشجرة.
استخدم النمط عندما تريد من كود العميل أن يعامل العناصر البسيطة والمعقدة بشكل موحّد.
جميع العناصر التي يحددها نمط المركَّب تشترك في واجهة مشتركة. باستخدام هذه الواجهة، لا يحتاج العميل إلى القلق بشأن الفئة المحددة للكائنات التي يتعامل معها.
##How to Implement
-
تأكد من أن النموذج الأساسي لتطبيقك يمكن تمثيله كهيكل شجري. حاول تقسيمه إلى عناصر بسيطة وحاويات. تذكر أن الحاويات يجب أن تكون قادرة على احتواء عناصر بسيطة وحاويات أخرى.
-
أعلن عن واجهة المكوّن بقائمة من الطرق التي تنطبق على كلٍّ من المكوّنات البسيطة والمعقدة.
-
أنشئ فئة ورقة لتمثيل العناصر البسيطة. قد يحتوي البرنامج على فئات ورقة متعددة ومختلفة.
-
أنشئ فئة حاوية لتمثيل العناصر المعقدة. في هذه الفئة، وفّر حقل مصفوفة لتخزين مراجع للعناصر الفرعية. يجب أن تكون المصفوفة قادرة على تخزين كل من الأوراق والحاويات، لذا تأكد من إعلانها بنوع واجهة المكوّن.
أثناء تنفيذ طرق واجهة المكوّن، تذكر أن الحاوية يُفترض أن تفوّض معظم العمل للعناصر الفرعية.
-
أخيراً، عرّف الطرق لإضافة وإزالة العناصر الفرعية في الحاوية.
ضع في اعتبارك أن هذه العمليات يمكن إعلانها في واجهة المكوّن. سيُنتهك هذا مبدأ فصل الواجهة لأن الطرق ستكون فارغة في فئة الورقة. ومع ذلك، سيتمكن العميل من معاملة جميع العناصر بالتساوي، حتى عند تكوين الشجرة.
##Pros & Cons
- يمكنك العمل مع هياكل الشجرة المعقدة بشكل أكثر ملاءمة: استفد من تعددية الأشكال والتكرار.
- مبدأ الفتح/الإغلاق. يمكنك إدخال أنواع عناصر جديدة في التطبيق دون كسر الكود الحالي الذي يعمل مع شجرة الكائنات.
- قد يكون من الصعب توفير واجهة مشتركة للفئات التي تختلف وظائفها كثيراً. في بعض السيناريوهات، ستحتاج إلى المبالغة في تعميم واجهة المكوّن، مما يجعل فهمها أصعب.
##Relations with Other Patterns
-
يمكنك استخدام المنشئ عند إنشاء أشجار المركَّب المعقدة لأنه يمكنك برمجة خطوات بنائه لتعمل بشكل متكرر.
-
كثيراً ما يُستخدم سلسلة المسؤولية جنباً إلى جنب مع المركَّب. في هذه الحالة، عندما تتلقى مكوّنة الورقة طلباً، قد تمرره عبر سلسلة جميع المكوّنات الأصلية وصولاً إلى جذر شجرة الكائنات.
-
يمكنك استخدام المُكرّرات للمرور عبر أشجار المركَّب.
-
يمكنك استخدام الزائر لتنفيذ عملية على شجرة المركَّب بأكملها.
-
يمكنك تنفيذ عُقد الأوراق المشتركة في شجرة المركَّب كـوزن خفيف لتوفير بعض ذاكرة الوصول العشوائي.
-
يتشابه المركَّب والمزخرف في مخططات الهيكل لأن كليهما يعتمد على التركيب المتكرر لتنظيم عدد مفتوح من الكائنات.
يشبه المزخرف المركَّب ولكن له مكوّن فرعي واحد فقط. ثمة فرق جوهري آخر: يضيف المزخرف مسؤوليات إضافية للكائن المُغلَّف، في حين أن المركَّب يجمع نتائج أبنائه فحسب.
ومع ذلك، يمكن للنمطين التعاون أيضاً: يمكنك استخدام المزخرف لتوسيع سلوك كائن محدد في شجرة المركَّب.
-
كثيراً ما تستفيد التصميمات التي تستخدم المركَّب والمزخرف بشكل مكثّف من استخدام النموذج الأولي. يتيح لك تطبيق النمط استنساخ الهياكل المعقدة بدلاً من إعادة بنائها من الصفر.