مبادئ SOLID في لغة C#: تطبيق متقدم وحالات عملية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: عندما تتوقف مبادئ SOLID عن كونها نظرية

\n

معظم المطورين على دراية باختصار SOLID على مستوى "يجب أن يكون الفصل مسؤولاً عن شيء واحد". ومع ذلك، في المشاريع الحقيقية بلغة C# (.NET) مع حقن التبعية (Dependency Injection)، والخدمات المصغرة، والأنماط غير المتزامنة، يؤدي الالتزام الساذج بالمبادئ إلى تأثير عكسي - التعقيد المفرط، وتضخم الواجهات، وانتهاك التغليف.

\n

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

\n\n

1. المسؤولية الفردية: حدود المسؤولية في DDD

\n

غالباً ما يتم الخلط بين مبدأ المسؤولية الفردية (SRP) وبين "فصل واحد - وظيفة واحدة". في الواقع، يتعلق الأمر بـ سبب التغيير (reason to change). في سياق C# والتصميم القائم على المجال (Domain-Driven Design)، هذا يعني أن الفصل يجب أن يتغير فقط لسبب واحد، مرتبط بفاعل واحد أو قاعدة عمل واحدة.

\n\n

النمط المضاد المتقدم: "الكيان الإلهي"

\n

لننظر إلى مثال نموذجي لانتهاك SRP في .NET - الفصل OrderEntity، الذي يدير في نفس الوقت حالة الطلب، ويحسب الضرائب، ويرسل البريد الإلكتروني، ويحفظ البيانات في قاعدة البيانات من خلال Entity Framework.

\n\n
// ❌ انتهاك SRP\npublic class OrderEntity\n{\n    public int Id { get; set; }\n    public decimal Total { get; set; }\n    public string Email { get; set; }\n\n    public void CalculateTax() { /* منطق الضرائب */ }\n    public void SendConfirmation() { /* إرسال البريد الإلكتروني */ }\n    public void SaveToDatabase() { /* EF Core SaveChanges */ }\n}\n
\n

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

\n\n

الحل: فصل الخدمات من خلال DI

\n
// ✅ SRP من خلال الخدمات\npublic class Order\n{\n    public int Id { get; set; }\n    public decimal Total { get; set; }\n    // الحالة فقط، لا منطق\n}\n\npublic class TaxCalculator : ITaxCalculator\n{\n    public decimal Calculate(Order order) { /* ... */ }\n}\n\npublic class EmailService : INotificationService\n{\n    public Task SendConfirmation(Order order) { /* ... */ }\n}\n\npublic class OrderRepository : IRepository<Order>\n{\n    public Task SaveAsync(Order order) { /* EF Core */ }\n}\n
\n

الآن كل فصل لديه سبب واحد بالضبط للتغيير. حاوية DI (Microsoft.Extensions.DependencyInjection) تربطهم من خلال المنشئات (constructors).

\n\n

2. مبدأ الإغلاق للفتح: التوسع من خلال الاستراتيجية والديكوراتور

\n

ينص مبدأ الإغلاق للفتح (OCP) على: يجب أن يكون الفصل مفتوحاً للتوسع، ولكنه مغلق للتعديل. في .NET، يتم تحقيق ذلك ليس فقط من خلال الوراثة، ولكن أيضاً من خلال التركيب مع أنماط Strategy و Decorator.

\n\n

الحالة المتقدمة: التحقق مع تزايد القواعد

\n

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

\n\n
// ❌ انتهاك OCP - تعديل الفصل الحالي\npublic class OrderValidator\n{\n    public bool Validate(Order order)\n    {\n        if (order.Total < 0) return false;\n        if (string.IsNullOrEmpty(order.Address)) return false;\n        // قاعدة جديدة - نقوم بتعديل الفصل\n        if (order.Total > 10000) return false;\n        return true;\n    }\n}\n
\n\n
// ✅ OCP من خلال تركيب المدققات\npublic interface IValidator<T>\n{\n    bool Validate(T entity);\n}\n\npublic class TotalValidator : IValidator<Order>\n{\n    public bool Validate(Order order) => order.Total > 0;\n}\n\npu                

المدونات

توصيات الكتب