التركيب بدلاً من الوراثة
جزء من قسم البرمجة كائنية التوجه في رحلة C# على Coddy. الدرس 41 من 70.
ينشئ التوريث اقترانًا وثيقًا بين الفئات. عندما ترث Car من Vehicle، يمكن لأي تغيير في Vehicle أن يتسبب في تعطل Car. يوفّر التركيب بديلًا أكثر مرونة: فبدلًا من توريث السلوك، تحتوي الفئة على كائنات توفّر ذلك السلوك.
تخيّل شخصية في لعبة يمكنها التحرك والهجوم. باستخدام الوراثة، قد تنشئ تسلسلاً هرميًا معقدًا. أما باستخدام التركيب، فكل ما عليك هو تزويد الشخصية بالمكوّنات التي تحتاج إليها:
public class MovementBehavior
{
public void Move() => Console.WriteLine("Moving...");
}
public class AttackBehavior
{
public void Attack() => Console.WriteLine("Attacking!");
}
public class Character
{
private MovementBehavior movement = new MovementBehavior();
private AttackBehavior attack = new AttackBehavior();
public void Move() => movement.Move();
public void Attack() => attack.Attack();
}الفرق الأساسي هو العلاقة. تمثّل الوراثة علاقة "is-a" (فالـ Dog is an Animal)، بينما يمثّل التركيب علاقة "has-a" (فإن Character has سلوك movement). يتيح لك التركيب تبديل السلوكيات أثناء وقت التشغيل، ودمج الإمكانات بحرية، وتجنّب مشكلة الفئة الأساسية الهشّة.
استخدم الوراثة عندما تكون هناك علاقة هرمية حقيقية. اختر التركيب عندما تحتاج إلى المرونة، أو عندما تكون السلوكيات قابلة للمشاركة بين فئات غير مترابطة، أو عندما تريد تغيير السلوك ديناميكيًا.
التحدي
سهللنبنِ نظامًا للإشعارات يوضّح قوة التركيب مقارنةً بالوراثة. بدلًا من إنشاء تسلسل هرمي معقّد من فئات الإشعارات، ستقوم بتركيب NotificationSender من خلال تزويده بمكوّنات سلوك قابلة للتبديل.
ستنظّم الشيفرة عبر ثلاثة ملفات:
Behaviors.cs: أنشئ فئتَي سلوك في مساحة الاسمNotificationsتتوليان جوانب مختلفة من إرسال الإشعارات:FormattingBehavior- تحتوي على أسلوبFormat(string message)يعيد الرسالة محاطةً بـ[ALERT]في البداية و[END]في النهايةDeliveryBehavior- تحتوي على أسلوبDeliver(string channel, string message)يعيد سلسلة نصية بالتنسيقSending via {channel}: {message}
NotificationSender.cs: أنشئ فئةNotificationSenderفي مساحة الاسمNotificationsتستخدم التركيب لدمج السلوكيات. يجب أن يقوم المرسِل بما يلي:- يحتوي على مثيلين من كلٍّ من
FormattingBehaviorوDeliveryBehaviorكحقلين خاصين - يحتوي على خاصية
Channel(من النوع string) تخزّن اسم قناة التسليم - يحتوي على مُنشئ يقبل اسم القناة
- يحتوي على أسلوب
Send(string message)ينسّق الرسالة أولًا باستخدام سلوك التنسيق، ثم يسلّمها باستخدام سلوك التسليم، ويعيد النتيجة النهائية
- يحتوي على مثيلين من كلٍّ من
Program.cs: في ملفك الرئيسي، أنشئNotificationSenderباستخدام قناة من الإدخال، ثم أرسل رسالة من الإدخال. اطبع النتيجة لإظهار كيفية عمل السلوكيات المركّبة معًا.
ستتلقى مدخلين:
- اسم قناة التسليم (مثل
EmailوSMS) - الرسالة المراد إرسالها
اطبع نتيجة استدعاء Send() على مرسل الإشعارات.
على سبيل المثال، إذا كان الإدخالان هما Email وServer is down، فيجب أن يكون الناتج:
Sending via Email: [ALERT] Server is down [END]لاحظ أن NotificationSender لا يرث من أي شيء، بل "has" ببساطة سلوكَي التنسيق والتسليم. ويعني أسلوب التركيب هذا أنه يمكنك بسهولة تبديل أيٍّ من السلوكين دون تغيير فئة المرسِل، أو إعادة استخدام هذين السلوكين في فئات مختلفة تمامًا!
جرّب بنفسك
using System;
using Notifications;
class Program
{
public static void Main(string[] args)
{
// اقرأ المدخلات
string channel = Console.ReadLine();
string message = Console.ReadLine();
// TODO: أنشئ NotificationSender بالقناة
// TODO: أرسل الرسالة واطبع النتيجة
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injectionتدرّب بنفسك: مترجم C# عبر الإنترنت