paint-brush
11 نمط تصميم رئيسي: دليل أساسيبواسطة@ssukhpinder
تاريخ جديد

11 نمط تصميم رئيسي: دليل أساسي

بواسطة Sukhpinder Singh33m2024/09/08
Read on Terminal Reader

طويل جدا؛ ليقرأ

اكتشف أسرار هندسة البرمجيات من خلال كتاب *إتقان هندسة البرمجيات: شرح 11 نمط تصميم رئيسي*. تم كتابة الكتاب بلغة البرمجة C# 8.0. يوجد 11 نمط تصميم يمكن استخدامها لإنشاء مصنع لإنشاء المصانع. نمط المصنع المجرد هو طريقة مصنع توسعة؛ يوصى باتباع طريقة المصنع.
featured image - 11 نمط تصميم رئيسي: دليل أساسي
Sukhpinder Singh HackerNoon profile picture
0-item
1-item
2-item
3-item
4-item

اكتشف أسرار هندسة البرمجيات مع إتقان هندسة البرمجيات: شرح 11 نمط تصميم رئيسي .

جدول المحتويات

  1. نمط التصميم — مصنع تجريدي
  • أهداف التعلم
  • ابدء
  • كيفية استخدام مزود المصنع المجرد؟
  • الناتج

2. نمط التصميم — المحول

  • حالة الاستخدام
  • أهداف التعلم
  • ابدء

3. نمط التصميم — Builder

  • حالة الاستخدام
  • أهداف التعلم
  • ابدء
  • كيفية استخدام نمط البناء من طريقة Main()
  • الناتج

4. كيفية استخدام نمط سلسلة المسؤولية

  • حالة الاستخدام
  • ابدء
  • كيفية استخدام نمط سلسلة المسؤولية؟
  • الناتج

5. نمط التصميم — المُزيّن

  • حالة الاستخدام
  • أهداف التعلم
  • ابدء
  • نمط الديكور في العمل
  • الكود الكامل
  • الناتج

6. نمط التصميم - طريقة المصنع

  • أهداف التعلم
  • ابدء
  • كيفية استخدام طريقة المصنع؟
  • الناتج

7. نمط التصميم — المُكرر

  • حالة الاستخدام
  • ابدء
  • نمط التكرار في العمل
  • الناتج

8. نمط التصميم - الوسيط

  • حالة الاستخدام
  • أهداف التعلم
  • ابدء
  • كيفية استخدام نمط الوسيط من الطريقة الرئيسية

9. نمط التصميم — المراقب

  • حالة الاستخدام
  • أهداف التعلم
  • ابدء
  • كيفية استخدام نمط المراقب؟
  • الناتج

10. نمط خاصية متقدم في C# 8.0

  • لنبدأ
  • برنامج مطابقة الأنماط باستخدام صيغة التبديل الجديدة
  • برنامج الاختبار
  • مخرجات وحدة التحكم

11. نمط التصميم — Singleton

  • أهداف التعلم
  • ابدء
  • الناتج
  • سلامة الخيط

نمط التصميم — مصنع تجريدي

وفقا لعصابة الأربعة، يمكن اعتبار أنماط المصانع المجردة بمثابة المصنع لإنشاء المصانع.


أهداف التعلم

  • ما هو نمط تصميم المصنع المجرد؟
  • كيفية كتابة الكود باستخدام نمط تصميم المصنع المجرد؟
  • كيفية إنشاء مزود مصنع؟
  • كيفية إنشاء تطبيق عميل (من الطريقة الرئيسية) يستخدم مزود المصنع

المتطلبات الأساسية

نمط المصنع المجرد هو مجرد طريقة مصنع تمديد؛ ومن المستحسن الاطلاع على طريقة المصنع قبل فهم تصميم المصنع المجرد.

  • المعرفة الأساسية لمفاهيم OOPS.
  • أي معرفة بلغة البرمجة.

ابدء

لنأخذ نفس المثال لأي بنك به أنواع حسابات مثل حسابات التوفير والحسابات الجارية. الآن، لنطبق المثال أعلاه باستخدام نمط تصميم المصنع المجرد.


أولاً، قم بتنفيذ واجهتي ISavingAccount وICurrentAccount على النحو التالي:


 public interface ISavingAccount{ } public interface ICurrentAccount{ }


وراثة الواجهة في الفئات أدناه


 public class CurrentAccount : ICurrentAccount { public CurrentAccount(string message) { Console.WriteLine(message); } } public class SavingsAccount : ISavingAccount { public SavingsAccount( string message) { Console.WriteLine(message); } }


دعنا نكتب فئة مجردة تحتوي على طرق مجردة لكل نوع حساب.


 public abstract class AccountTypeFactory { public abstract ISavingAccount SavingAccountFactory(string message); public abstract ICurrentAccount CurrentAccountFactory(string message); }


الآن، دعنا نقوم بإنشاء تنفيذ مصنع يسمى "Bank1Factory"، والذي يوفر تنفيذ الأساليب المجردة.


 public class Bank1Factory : AccountTypeFactory { public override ICurrentAccount CurrentAccountFactory(string message) { return new CurrentAccount(message); } public override ISavingAccount SavingAccountFactory(string message) { return new SavingsAccount(message); } }


يختلف نمط تصميم المصنع المجرد عن طريقة المصنع التي يحتاجها لتنفيذ مزود المصنع، والذي يقوم بإرجاع المصانع حسب التعريف.


الآن بعد أن قمنا بإنشاء جميع التجريدات والمصانع، فلنقم بتصميم موفر المصنع. يُرجى الاطلاع أدناه على مقتطف التعليمات البرمجية لموفر المصنع، حيث ستقوم طريقة ثابتة بإنشاء مصنع بناءً على اسم الحساب.


 public class AccountFactoryProvider { public static AccountTypeFactory GetAccountTypeFactory(string accountName) { if (accountName.Contains("B1")) { return new Bank1Factory(); } else return null; } }

كيفية استخدام مزود المصنع المجرد؟

لنأخذ مثالاً لقائمة أرقام الحسابات حيث إذا كان اسم الحساب يتكون من " B1 " حرفيًا، فسوف يستخدم مثيل Bank1Factory الذي تم إرجاعه عبر موفر المصنع.

 static void Main(string[] args) { List<string> accNames = new List<string> { "B1-456", "B1-987", "B2-222" }; for (int i = 0; i < accNames.Count; i++) { AccountTypeFactory anAbstractFactory = AccountFactoryProvider.GetAccountTypeFactory(accNames[i]); if (anAbstractFactory == null) { Console.WriteLine("Invalid " + (accNames[i])); } else { ISavingAccount savingAccount = anAbstractFactory.SavingAccountFactory("Hello saving"); ICurrentAccount currentAccount = anAbstractFactory.CurrentAccountFactory("Hello Current"); } } Console.ReadLine(); }


إذا كان اسم الحساب لا يحتوي على الحرف "B1"، فسيقوم البرنامج بإخراج {{accountName}} غير صالح

الناتج

يرجى إيجاد أدناه الناتج من مقتطف الكود أعلاه.


 Hello saving B1-456 Hello Current B1-456 Hello saving B1-987 Hello Current B1-987

نمط التصميم — المحول

وفقًا لـ Gang of Four، يقوم نمط المحول بتحويل واجهات فئة إلى واجهات يتطلبها العميل.


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

حالة الاستخدام

لنتأمل مثالاً عن اندماج منظمتين؛ حيث تتولى المنظمة X مسؤولية المنظمة Y، ولكن أثناء دمج التعليمات البرمجية، لا تتوافق الواجهات. لنفترض أن الواجهة التي توفر قائمة بالمعاملات الخاصة بالمنظمة Y غير متوافقة مع المنظمة X.


لذا، يساعد نمط تصميم المحول في حل هذه المشكلة، حيث يعد تنفيذها بسيطًا للغاية.

أهداف التعلم

  • كيفية البرمجة باستخدام نمط تصميم المحول؟

ابدء

لنقم بإنشاء قائمة بالمعاملات من المؤسسة Y التي يتم تحويلها إلى أنماط يتطلبها تطبيق العميل للمؤسسة X. تُعرف الفئة أعلاه باسم "Adaptee".


 public class OrgYTransactions { public List<string> GetTransactionsList() { List<string> transactions = new List<string>(); transactions.Add("Debit 1"); transactions.Add("Debit 2"); transactions.Add("Debit 3"); return transactions; } }


ثانياً، دعونا نقوم بإنشاء واجهة مستهدفة.


 public interface ITransactions{ List<string> GetTransactions(); }


الآن أخيرًا، دعونا ننفذ فئة المحول على النحو التالي.


 public class TransAdapter : OrgYTransactions, ITransactions { public List<string> GetTransactions() { return GetTransactionsList(); } }


بعد الانتهاء من جميع التنفيذات المذكورة أعلاه، دعونا نفهم كيفية استخدام فئة المحول في تطبيق وحدة التحكم.


 class Program { static void Main(string[] args) { ITransactions adapter = new TransAdapter(); foreach (var item in adapter.GetTransactions()) { Console.WriteLine(item); } } }


إذا نظرت عن كثب إلى الاستخدام أدناه، فقد استخدمنا واجهة الهدف ITransactions وفئة المحول TransAdapter دون النظر في شكل واجهات فئة OrgYTransactions التابعة لجهة خارجية. هذه هي قوة نمط تصميم المحول، فهو يحول واجهات فئة إلى واجهات يحتاجها العميل.

نمط التصميم — Builder

وفقًا لـ Gang of Four، فإن نمط الإبداع "Builder" يسمح للشخص بفصل وإعادة استخدام طريقة محددة لبناء شيء ما.


حالة الاستخدام

لنأخذ مثالاً لسيارة، ويريد المستخدم بناء نموذجين، أي سيارة رياضية متعددة الاستخدامات وسيارة سيدان.


يُعد نمط تصميم الباني مفيدًا في حالة الاستخدام المذكورة أعلاه، ولنرى عرضًا توضيحيًا خطوة بخطوة.


تتمتع فئة السيارة بالخصائص التالية:

 public class Car{ public string Name { get; set; } public double TopSpeed { get; set; } public bool IsSUV { get; set; } }

أهداف التعلم

  • كيفية البرمجة باستخدام نمط تصميم البناء؟

ابدء

أولاً، دعنا ننفذ منشئ فئة مجردة ممتد بواسطة نماذج سيارات مختلفة مثل سيارات الدفع الرباعي أو سيارات السيدان حسب حالة الاستخدام.


 public abstract class CarBuilder { protected readonly Car _car = new Car(); public abstract void SetName(); public abstract void SetSpeed(); public abstract void SetIsSUV(); public virtual Car GetCar() => _car; }


تتكون الفئة المجردة من الطرق التالية

  • طرق مجردة لكل خاصية من خصائص فئة السيارة.
  • طريقة افتراضية تقوم بإخراج مثيل فئة Car.


الآن، دعنا نقوم بإنشاء مصنع يستخدم فئة CarBuilder لبناء نماذج سيارات مختلفة ويعيد مثيل السيارة المصنعة.


 public class CarFactory { public Car Build(CarBuilder builder) { builder.SetName(); builder.SetSpeed(); builder.SetIsSUV(); return builder.GetCar(); } }


وأخيرا، تنفيذ نماذج مختلفة من السيارات.

نموذجSuv.cs

 public class ModelSuv : CarBuilder { public override void SetIsSUV() { _car.IsSUV = true; } public override void SetName() { _car.Name = "Maruti SUV"; } public override void SetSpeed() { _car.TopSpeed = 1000; } }

نموذجSedan.cs

 public class ModelSedan : CarBuilder { public override void SetIsSUV() { _car.IsSUV = false; } public override void SetName() { _car.Name = "Maruti Sedan"; } public override void SetSpeed() { _car.TopSpeed = 2000; } }

كيفية إنشاء نمط المستخدم من طريقة Main()

أخيرًا، دعنا نستخدم أنماط التصميم لبناء نماذج سيارات مختلفة بمساعدة طريقة factory.Build(<model>).


 static void Main(string[] args) { var sedan = new ModelSedan(); var suv = new ModelSuv(); var factory = new CarFactory(); var builders = new List<CarBuilder> { suv, sedan }; foreach (var b in builders) { var c = factory.Build(b); Console.WriteLine($"The Car details" + $"\n--------------------------------------" + $"\nName: {c.Name}" + $"\nIs SUV: {c.IsSUV}" + $"\nTop Speed: {c.TopSpeed} mph\n"); } }

يوضح الاستخدام أعلاه مدى قدرتنا على بناء نماذج سيارات مختلفة بسهولة باستخدام نمط تصميم الباني.


نمط الكود قابل للصيانة والتوسعة بدرجة كبيرة. إذا احتجنا في المستقبل إلى تطوير نموذج جديد، فما يحتاجه النموذج الجديد هو توسيع فئة CarBuilder، ويتم ذلك.

الناتج

كيفية استخدام نمط سلسلة المسؤولية

وفقًا لـ Gang of Four، فإنه يحدد سلسلة من المسؤوليات لمعالجة الطلب. بعبارة أخرى، قم بنقل الطلب من كائن إلى آخر حتى يقبل الكائن مسؤوليته.


حالة الاستخدام

لنتأمل مثالاً لنظام المطالبات في أي شركة. وفيما يلي قائمة بنطاق الأسعار التي يمكن الموافقة عليها ومن الذي يمكنه الموافقة عليها.


 100–1000 Rs => Junior/Senior Engineers => Approved by Manager 1001–10000 Rs => Managers => Approved by Senior Manager


إذا كان المبلغ خارج نطاق 10000، فإنه يتطلب موافقة استثنائية من المدير الأعلى.


يمكن تنفيذ حالة الاستخدام المذكورة أعلاه بسهولة باستخدام نمط تصميم سلسلة المسؤولية. لذا، تتمتع فئة المطالبة بالخصائص التالية.


 public class Claim{ public int Id{get;set;} public double amount{get;set;} }

ابدء

أولاً، دعنا نحدد الوظائف التي يمكن للموافق على المطالبة القيام بها ونضع تسلسلًا هرميًا للموظفين على مستويات مختلفة. قم بتنفيذ فئة مجردة كما هو موضح أدناه


 public abstract class ClaimApprover { protected ClaimApprover claimApprover; public void SetHierarchy(ClaimApprover claimApprover) { this.claimApprover = claimApprover; } public abstract void ApproveRequest(Claim claim); }


وفقًا لحالة الاستخدام، فلنقم بتشغيل مقدم الطلب من فئة "صغار/كبار السن". لاحظ أن هذه الفئة/التعيين من الموظفين لا يمكنها الموافقة على أي مطالبات.


 public class Junior : ClaimApprover { public override void ApproveRequest(Claim claim) { System.Console.WriteLine("Cannot approve"); } }


وبالمثل، دعنا نحدد التنفيذ لأدوار المدير والمدير الأول.


 public class Manager : ClaimApprover { public override void ApproveRequest(Claim claim) { if (claim.amount >= 100 && claim.amount <= 1000) { System.Console.WriteLine($"Claim reference {claim.Id} with amount {claim.amount} is approved by Manager"); } else if (claimApprover != null) { claimApprover.ApproveRequest(claim); } } }


لاحظ أنه بناءً على نطاق المبلغ، إذا كان ضمن نطاق المدير، فيمكن للمدير الموافقة على المطالبة؛ وإلا، سيتم تمرير الطلب إلى المدير الأعلى.


 public class SeniorManager : ClaimApprover { public override void ApproveRequest(Claim claim) { if (claim.amount > 1000 && claim.amount <= 10000) { System.Console.WriteLine($"Claim reference {claim.Id} with amount {claim.amount} is approved by Senior Manager"); } else { System.Console.WriteLine($"Exceptional approval for Claim reference {claim.Id} with amount {claim.amount} is approved by Senior Manager"); } } }


وبالمثل، إذا كان نطاق المبلغ يقع ضمن نطاق المدير الأعلى، فيمكن للمدير الموافقة على المطالبة؛ وإلا، كونه الأخير في التسلسل الهرمي، يتم إجراء موافقة استثنائية لمبلغ خارج النطاق.


 ClaimApprover junior = new Manager(); ClaimApprover sukhpinder = new Manager(); ClaimApprover singh = new SeniorManager(); junior.SetHierarchy(sukhpinder); sukhpinder.SetHierarchy(singh); Claim c1 = new Claim() { amount = 999, Id = 1001 }; Claim c2 = new Claim() { amount = 10001, Id = 1002 }; junior.ApproveRequest(c1); sukhpinder.ApproveRequest(c2);

كيفية استخدام نمط سلسلة المسؤولية؟

  1. تعريف الجهة التي توافق على المطالبة: مبتدئ، على الرغم من أنه لا يمكنها الموافقة على أي مطالبات.
  2. قم بتحديد الجهة التي وافقت على المطالبة: المدير "sukhpinder".
  3. قم بتعريف الجهة التي توافق على المطالبة: المدير الأول "سينغ".
  4. إنشاء علاقة تسلسل هرمي للصغار، أي أن الموافق على المطالبات هو المدير.
  5. إنشاء علاقة تسلسل هرمي للمدير، أي أن الشخص الذي يوافق على المطالبات هو المدير الأعلى.
  6. إنشاء نطاقين مختلفين للمطالبات.
  7. يرسل جونيور طلب المطالبة إلى المدير.
  8. يقوم المدير بإرسال طلب المطالبة إلى المدير الأعلى.

الناتج

 Claim reference 1001 with amount 999 is approved by Manager Exceptional approval for Claim reference 1002 with amount 10001 is approved by Senior Manager


بالنسبة لمخرجات الخط الأول، كان المبلغ ضمن النطاق، لذلك وافق عليه المدير.


بالنسبة لمخرجات الخط الثاني، على الرغم من موافقة المدير الأعلى عليها، إلا أن المبلغ كان خارج النطاق.

نمط التصميم — مُزيّن

وفقًا لـGang of Four، يضيف النمط مسؤوليات إضافية إلى كائن الفئة بشكل ديناميكي.


حالة الاستخدام

لنأخذ مثال شراء سيارة بقيمة عشرة لاك روبية؛ حيث توفر الشركة الميزات الإضافية التالية.

  • فتحة السقف
  • نظام الموسيقى المتقدم
  • و الكثير غير ذلك


مع بعض الميزات الإضافية، يزداد السعر الإجمالي للسيارة. دعنا ننفذ حالة الاستخدام المذكورة أعلاه باستخدام نمط الديكور.

أهداف التعلم

  • كيفية البرمجة باستخدام نمط تصميم الديكور؟

ابدء

دعنا ننفذ حالة الاستخدام المحددة أعلاه. أولاً، قم بتعريف فئة مجردة Car وطرقها الأساسية.


 public abstract class Car{ public abstract int CarPrice(); public abstract string GetName(); }


خذ في الاعتبار سيارة صغيرة تمتد فوق فئة السيارة المجردة.


 public class SmallCar : Car{ public override int CarPrice() => 10000; public override string GetName() => "Alto Lxi"; }


الآن، قم بتنفيذ فئة CarDecorator باستخدام مكون Car.


 public class CarDecorator : Car { protected Car _car; public CarDecorator(Car car) { _car = car; } public override int CarPrice() => _car.CarPrice(); public override string GetName() =>_car.GetName(); }


الآن، دعنا نقوم بإنشاء فئة منفصلة لكل ميزة إضافية متاحة لـ Car التي ترث فئة CarDecorator.


وبحسب حالة الاستخدام، فإن الميزات الإضافية هي فتحة سقف ونظام موسيقى متقدم.

برنامج AdvanceMusic.cs

تجاوز الأساليب مثل

  • أضف التكلفة الإضافية لنظام الموسيقى المتقدم إلى السعر الإجمالي للسيارة.

  • تحديث اسم السيارة مع اسم الميزة الإضافية.

     public class AdvanceMusic : CarDecorator { public AdvanceMusic(Car car) : base(car) { } public override int CarPrice() => _car.CarPrice() + 3000; public override string GetName()=> "Alto Lxi with advance music system"; }

فتحة سقف.

تجاوز الأساليب مثل

  • أضف التكلفة الإضافية لـ "فتحة السقف" إلى السعر الإجمالي للسيارة.
  • تحديث اسم السيارة مع اسم الميزة الإضافية.
 public class Sunroof : CarDecorator { public Sunroof(Car car) : base(car) { } public override int CarPrice() => _car.CarPrice() + 2000; public override string GetName() => "Alto Lxi with Sunroof"; }

نمط الديكور في العمل

إنشاء مثيل لـ SmallCar وإخراج اسم وسعر السيارة.


 Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice());


الآن، دعنا نضيف ميزات إضافية كما هو موضح أدناه


 var car1 = new Sunroof(car); var car2 = new AdvanceMusic(car);

الكود الكامل

 static void Main(string[] args) { Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice()); var car1 = new Sunroof(car); Console.WriteLine($"Price of car {car1.GetName()} : " + car1.CarPrice()); var car2 = new AdvanceMusic(car); Console.WriteLine($"Price of car {car2.GetName()} : " + car2.CarPrice()); }

الناتج

مبروك..!! لقد قمت بنجاح بتنفيذ حالة الاستخدام باستخدام نمط الديكور.

نمط التصميم — طريقة المصنع

وفقًا لـ Gang of Four، تسمح طريقة المصنع للفئة الفرعية بتحديد كائن الفئة الذي يجب إنشاؤه.


أهداف التعلم

  • ما هو نمط تصميم طريقة المصنع؟
  • كيفية كتابة الكود باستخدام طريقة المصنع؟

ابدء

لنأخذ مثالاً لأي بنك به أنواع حسابات مثل حسابات التوفير والحسابات الجارية. الآن، دعنا ننفذ المثال أعلاه باستخدام نمط تصميم المصنع


أولاً، قم بإنشاء فئة مجردة من نوع الحساب.


 public abstract class AccoutType { public string Balance { get; set; } }


قم بتنفيذ فئات الحسابات الجارية وحسابات التوفير التي ترث الفئة المجردة AccountType كما هو موضح أدناه.


 public class SavingsAccount : AccoutType { public SavingsAccount() { Balance = "10000 Rs"; } } public class CurrentAccount : AccoutType { public CurrentAccount() { Balance = "20000 Rs"; } }


أخيرًا، دعنا ننفذ واجهة المصنع، والتي ستوفر عقدًا يساعد في إنشاء كائن فئة. تُعرف هذه الواجهة أيضًا باسم Creator.


 public interface IAccountFactory { AccoutType GetAccoutType(string accountName); }


أخيرًا، اكتب تنفيذًا لطريقة واجهة المنشئ كما هو موضح أدناه. تُعرف الفئة التي تنفذ المنشئ باسم Concrete Creator.


 public class AccountFactory : IAccountFactory { public AccoutType GetAccoutType(string accountName) { if (accountName.Equals("SAVINGS", StringComparison.OrdinalIgnoreCase)) { return new SavingsAccount(); } else if (accountName.Equals("CURRENT", StringComparison.OrdinalIgnoreCase)) { return new CurrentAccount(); } else { throw new ArgumentException("Invalid account name"); } } }


هذا كل شيء. لقد قمت بنجاح بتنفيذ طريقة المصنع باستخدام مثال البنك.

كيفية استخدام طريقة المصنع؟

ستقرر الفئة الفرعية أي كائن فئة "AccountType" سيتم إنشاؤه استنادًا إلى اسم الحساب.


 class Program { static void Main(string[] args) { IAccountFactory accountFactory = new AccountFactory(); var savingAccount = accountFactory.GetAccoutType("SAVINGS"); Console.WriteLine("Saving account balance: " + savingAccount.Balance); var currentAccount = accountFactory.GetAccoutType("CURRENT"); Console.WriteLine("Current account balance: " + currentAccount.Balance); } }

على سبيل المثال، إذا كان اسم الحساب هو "SAVINGS"، فسيتم إنشاء كائن الفئة "SavingAccount" وإرجاعه.


وبالمثل، إذا كان اسم الحساب هو "CURRENT"، فسيتم إنشاء كائن فئة "CurrentAccount" وإرجاعه.

الناتج

 Saving account balance: 10000 Rs Current account balance: 20000 Rs

نمط التصميم — المُكرر

وفقًا لـ Gang of Four، يوفر نمط التكرار عملية للحصول على كائن المجمع دون معرفة تنفيذه.


حالة الاستخدام

دعنا نأخذ مثالاً لقائمة مجموعة من السيارات وstring[] مجموعة من الدراجات النارية، نحتاج إلى تصميم كائن مجمع حتى يتمكن المرء من التكرار عبر المجموعة دون معرفة ما إذا كانت قائمة أو مجموعة.


يساعد نمط تصميم المُكرر في حل هذه المشكلة حيث سيتنقل المُكرر القياسي عبر أنواع مجموعات مختلفة.

ابدء

بالنظر إلى حالة الاستخدام المذكورة أعلاه، دعنا نقوم بتعريف واجهة متكررة مخصصة تعمل كطبقة مجردة فوق متكرر القائمة والمصفوفة.


 public interface IVehicleIterator{ void First(); bool IsDone(); string Next(); string Current(); }


الآن، اكتب متكررات السيارات والدراجات النارية التي تنفذ الواجهة المذكورة أعلاه وفقًا لحالة الاستخدام.

برنامج CarIterator.cs

 public class CarIterator : IVehicleIterator { private List<string> _cars; private int _current; public CarIterator(List<string> cars) { _cars = cars; _current = 0; } public string Current() { return _cars.ElementAt(_current); } public void First() { _current = 0; } public bool IsDone() { return _current >= _cars.Count; } public string Next() { return _cars.ElementAt(_current++); } }


يتم تنفيذ متكرر السيارة عبر مجموعة List<string> ويوفر تنفيذًا لأساليب الواجهة.

برنامج MotorcycleIterator.cs

يتم تنفيذ متكرر الدراجة النارية عبر مجموعة string[] ويوفر تنفيذًا لأساليب الواجهة.


 public class MotercycleIterator : IVehicleIterator { private string[] _motercylces; private int _current; public MotercycleIterator(string[] motercylces) { _motercylces = motercylces; _current = 0; } public string Current() { return _motercylces[_current]; } public void First() { _current = 0; } public bool IsDone() { return _current >= _motercylces.Length; } public string Next() { return _motercylces[_current++]; } }


بعد تعريف كل المتكررات المذكورة أعلاه، قم بتعريف واجهة كائن مجمع قياسية تقوم بإنشاء المتكررات.


 public interface IVehicleAggregate{ IVehicleIterator CreateIterator(); }


أخيرًا، اكتب الفئات التي تنفذ واجهة التجميع المذكورة أعلاه. وفقًا لحالة الاستخدام، ستنفذ كل من فئتي السيارة والدراجة النارية واجهة التجميع.

سيارة. سي اس

تعيد طريقة واجهة المجمع المُكرر ذي الصلة كما هو موضح أدناه.


 public class Car : IVehicleAggregate { private List<string> _cars; public Car() { _cars = new List<string> { "Car 1", "Car 2", "Car 3" }; } public IVehicleIterator CreateIterator() { return new CarIterator(_cars); } }

دراجة نارية.

تعيد طريقة واجهة المجمع المُكرر ذي الصلة كما هو موضح أدناه.


 public class Motercycle : IVehicleAggregate { private string[] _motercycles; public Motercycle() { _motercycles = new[] { "Bike 1", "Bike 2", "Bike 3" }; } public IVehicleIterator CreateIterator() { return new MotercycleIterator(_motercycles); } }

نمط التكرار في العمل

تتحقق طرق PrintVehicles مما إذا كان !iterator.isDone ثم تقوم بإخراج عنصر المجموعة. بغض النظر عن المجموعة التي نتعامل معها، قم بتنفيذ طرق مثل First وIsDone وNext.


 static void Main(string[] args) { IVehicleAggregate car = new Vehicles.Car(); IVehicleAggregate motercycle = new Vehicles.Motercycle(); IVehicleIterator carIterator = car.CreateIterator(); IVehicleIterator motercycleIterator = motercycle.CreateIterator(); PrintVehicles(carIterator); PrintVehicles(motercycleIterator); } static void PrintVehicles(IVehicleIterator iterator) { iterator.First(); while (!iterator.IsDone()) { Console.WriteLine(iterator.Next()); } }

الناتج

لا نعرف نوع المجموعة الأساسية، ولكن لا يزال يتم تكرارها عبر نمط تصميم المُكرر. إذا قمت بالمتابعة والتشغيل، فسوف يعرض الناتج التالي.

نمط التصميم — الوسيط

وفقًا لـ Gang of Four، فإن نمط الوسيط يجسد تفاعل الكائنات مع بعضها البعض.


يساعدنا نمط تصميم الوسيط في تصميم تطبيقات مرتبطة بشكل فضفاض من خلال تغليف تفاعلات الكائنات.

حالة الاستخدام

دعونا نفكر في مثال لغرفة دردشة حيث يقوم المشاركون بالتسجيل، وكيفية التواصل بشكل فعال.


يجب تنفيذ محادثة غرفة الدردشة التالية باستخدام نمط تصميم الوسيط.


 David to Scott: 'Hey' Scott to David: 'I am good how about you.' Jennifer to Ashley: 'Hey ashley... david is back in the group' Jennifer to David: 'Where have you been?' Ashley to David: 'How come you aren't active here anymore?'

أهداف التعلم

  • كيفية البرمجة باستخدام نمط التصميم الوسيط؟

ابدء

الخطوة الأولى هي إنشاء قائمة بأسماء المستخدمين التي سيتم استخدامها داخل غرفة الدردشة. يظهر أدناه تعداد عام لهذه الأسماء.


 public enum Username{ Ashley, David, Jennifer, Scott }


الآن، أولاً وقبل كل شيء، قم بتنفيذ طبقة مجردة لغرفة الدردشة.


 public abstract class AChatroom { public abstract void Register(User user); public abstract void Post(string fromUser, string toUser, string msg); }


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


 public class Chatroom : AChatroom { private Dictionary<string, User> _users = new Dictionary<string, User>(); public override void Post(string fromUser, string toUser, string msg) { User participant = _users[toUser]; if (participant != null) { participant.DM(fromUser, msg); } } public override void Register(User user) { if (!_users.ContainsValue(user)) { _users[user.Name] = user; } user.Chatroom = this; } }


أخيرًا، دعنا ننفذ الإجراءات التي يمكن للمستخدم القيام بها، مثل نشر رسالة إلى مستخدم في غرفة الدردشة أو تلقي رسالة مباشرة من مستخدم آخر.


 public class User { private Chatroom _chatroom; private string _name; public User(string name) => this._name = name; public string Name => _name; public Chatroom Chatroom { set { _chatroom = value; } get => _chatroom; } public void Post(string to, string message) => _chatroom.Post(_name, to, message); public virtual void DM(string from, string message) => Console.WriteLine("{0} to {1}: '{2}'", from, Name, message); }

كيفية استخدام نمط الوسيط من الطريقة الرئيسية

 static void Main(string[] args) { Chatroom chatroom = new Chatroom(); User Jennifer = new UserPersona(Username.Jennifer.ToString()); User Ashley = new UserPersona(Username.Ashley.ToString()); User David = new UserPersona(Username.David.ToString()); User Scott = new UserPersona(Username.Scott.ToString()); chatroom.Register(Jennifer); chatroom.Register(Ashley); chatroom.Register(David); chatroom.Register(Scott); David.Post(Username.Scott.ToString(), "Hey"); Scott.Post(Username.David.ToString(), "I am good how about you."); Jennifer.Post(Username.Ashley.ToString(), "Hey ashley... david is back in the group"); Jennifer.Post(Username.David.ToString(), "Where have you been?"); Ashley.Post(Username.David.ToString(), "How come you aren't active here anymore?"); Console.ReadKey(); }


  1. تم إنشاء كائن فئة غرفة الدردشة.
  2. يتم إنشاء أربعة مستخدمين مختلفين بأسماء فريدة.
  3. سجل كل واحد منهم في غرفة الدردشة.
  4. أصبح بإمكان المستخدمين الآن البدء في نشر الرسائل لبعضهم البعض.

يصف تنفيذ البرنامج فقط طريقة Post الخاصة بفئة المستخدم.


الإخراج: سجل غرفة الدردشة لتنفيذ البرنامج المذكور أعلاه


 David to Scott: 'Hey' Scott to David: 'I am good how about you.' Jennifer to Ashley: 'Hey ashley... david is back in the group' Jennifer to David: 'Where have you been?' Ashley to David: 'How come you aren't active here anymore?'

نمط التصميم — المراقب

وفقًا لـ Gang of Four، يحدد نمط المراقب التبعية بين كائنين أو أكثر. لذا، عندما تتغير حالة أحد الكائنات، يتم إخطار جميع الكائنات التابعة له.


بعبارة أخرى، يؤدي التغيير في كائن واحد إلى بدء الإشعار في كائن آخر.

حالة الاستخدام

لنأخذ مثالاً لشخصية مؤثرة على إنستغرام لديها عدد " x " من المتابعين. لذا، في اللحظة التي يضيف فيها الشخص المشهور منشورًا، يتم إخطار جميع المتابعين.


دعونا ننفذ حالة الاستخدام المذكورة أعلاه باستخدام نمط تصميم المراقب.

أهداف التعلم

  • كيفية البرمجة باستخدام نمط تصميم المراقب؟

ابدء

وفقًا لحالة الاستخدام، يتم تنفيذ واجهة تحتوي على الإجراءات التي يمكن للمشاهير تنفيذها. تُعرف باسم " الموضوع ".


 public interface ICelebrityInstagram{ string FullName { get; } string Post { get; set; } void Notify(string post); void AddFollower(IFollower fan); void RemoveFollower(IFollower fan); }

يحتوي الموضوع على الوظائف الأعضاء التالية.

  • إعلام: لإعلام جميع المتابعين.

  • إضافة متابع: قم بإضافة متابع جديد إلى قائمة المشاهير.

  • إزالة المتابع: إزالة المتابع من قائمة المشاهير.


الآن، قم بتنفيذ واجهة المراقب "IFollower"، التي تحتوي على دالة العضو "Update" للإشعارات.


 public interface IFollower{ void Update(ICelebrityInstagram celebrityInstagram); }


أخيرًا، حان الوقت لتطبيق "التنفيذ الملموس" لكل من " الموضوع " و" المراقب ".

تم تسمية ConcreteObserver باسم "Follower.cs"

إنه يوفر تنفيذًا لوظيفة العضو Update، والتي تقوم بإخراج اسم المشاهير ومنشوراتهم إلى وحدة التحكم.


 public class Follower : IFollower { public void Update(ICelebrityInstagram celebrityInstagram) { Console.WriteLine($"Follower notified. Post of {celebrityInstagram.FullName}: " + $"{celebrityInstagram.Post}"); } }

موضوع ConcreteSubject اسمه "Sukhpinder.cs"

 public class Sukhpinder : ICelebrityInstagram { private readonly List<IFollower> _posts = new List<IFollower>(); private string _post; public string FullName => "Sukhpinder Singh"; public string Post { get { return _post; } set { Notify(value); } } public void AddFollower(IFollower follower) { _posts.Add(follower); } public void Notify(string post) { _post = post; foreach (var item in _posts) { item.Update(this); } } public void RemoveFollower(IFollower follower) { _posts.Remove(follower); } }

كيفية تحديد نمط المراقب؟

تظهر حالة الاستخدام التالية أنه عندما يتم تنفيذ العبارة أدناه sukhpinder.Post = “I love design patterns.”; يتم تشغيل طريقة التحديث لكل متابع، أي يتم إخطار كل كائن متابع بمنشور جديد من “Sukhpinder”.


 static void Main(string[] args) { var sukhpinder = new Sukhpinder(); var firstFan = new Follower(); var secondFan = new Follower(); sukhpinder.AddFollower(firstFan); sukhpinder.AddFollower(secondFan); sukhpinder.Post = "I love design patterns."; Console.Read(); }

الناتج

نمط خاصية متقدم في لغة C# 8.0

تصف المقالة كيف يوفر مطابقة الأنماط طريقة فعالة لاستخدام البيانات ومعالجتها في النماذج التي لم تكن جزءًا من النظام الأساسي.


لنبدأ

دعنا نأخذ مثالاً لحاسبة الرسوم ونرى كيف يساعد مطابقة الأنماط في كتابة خوارزمية لذلك.

فئة الكيان المستخدمة في جميع أنحاء المقالة

 public class Car { public int PassengerCount { get; set; } } public class DeliveryTruck { public int Weight { get; set; } } public class Taxi { public int Fare { get; set; } } public class Bus { public int Capacity { get; set; } public int RidersCount { get; set; } }


المثال 1: احسب أجرة المرور وفقًا للشروط التالية:


  • إذا كانت السيارة سيارة => 100 روبية
  • إذا كانت السيارة هي DeliveryTruck => 200 روبية
  • إذا كانت السيارة حافلة => 150 روبية
  • إذا كانت السيارة عبارة عن سيارة أجرة => 120 روبية

برنامج مطابقة الأنماط باستخدام بناء جملة التبديل الجديد

إذا كان نوع السيارة يتطابق مع السيارة 100، يتم إرجاع & وما إلى ذلك. لاحظ أن null و{} هما حالتان افتراضيتان لنوع الكائن.

يمكن أيضًا استخدام "_" لبرمجة السيناريو الافتراضي. راجع بناء جملة التبديل الجديد.


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


 public static int TollFare(Object vehicleType) => vehicleType switch { Car c => 100, DeliveryTruck d => 200, Bus b => 150, Taxi t => 120, null => 0, { } => 0 };

اختبار البرنامج أعلاه

أمثلة للاختبار من وجهة نظر تطبيق وحدة التحكم. يوضح الكود أدناه كيفية استدعاء دالة مطابقة الأنماط أعلاه من الطريقة الرئيسية.


 var car = new Car(); var taxi = new Taxi(); var bus = new Bus(); var truck = new DeliveryTruck(); Console.WriteLine($"The toll for a car is {TollFare(car)}"); Console.WriteLine($"The toll for a taxi is {TollFare(taxi)}"); Console.WriteLine($"The toll for a bus is {TollFare(bus)}"); Console.WriteLine($"The toll for a truck is {TollFare(truck)}");

مخرجات وحدة التحكم

 The toll for a car is 100 The toll for a taxi is 120 The toll for a bus is 150 The toll for a truck is 200


المثال 2: إضافة تسعير الإشغال بناءً على نوع السيارة


  • تدفع السيارات وسيارات الأجرة التي لا تحمل ركابًا مبلغًا إضافيًا قدره 10 روبية.
  • تحصل السيارات وسيارات الأجرة التي تحمل راكبين على خصم بقيمة 10 روبية.
  • تحصل السيارات وسيارات الأجرة التي تحمل ثلاثة ركاب أو أكثر على خصم بقيمة 20 روبية.
  • تدفع الحافلات التي تقل عن 50% من الركاب رسومًا إضافية قدرها 30 روبية.
  • تحصل الحافلات التي تحتوي على أكثر من 90% من الركاب على خصم 40 روبية.
  • يتم فرض رسوم إضافية قدرها 100 روبية على الشاحنات التي يزيد وزنها عن 5000 رطل.
  • الشاحنات الخفيفة التي يقل وزنها عن 3000 رطل، تحصل على خصم بقيمة 20 روبية.

مفتاح مطابقة الأنماط

راجع قواعد مطابقة الأنماط مع فئات الخصائص الفردية والمتعددة. الرابط

مطابقة الأنماط — كيان السيارة

 Car { PassengerCount: 0 } => 100 + 10, Car { PassengerCount: 1 } => 100, Car { PassengerCount: 2 } => 100 - 10, Car c => 100 - 20,

مطابقة الأنماط — كيان التاكسي

 Taxi {Fare:0 }=>100+10, Taxi { Fare: 1 } => 100, Taxi { Fare: 2 } => 100 - 10, Taxi t => 100 - 20,

مطابقة الأنماط — كيان الحافلة

 Bus b when ((double)b.RidersCount / (double)b.Capacity) < 0.50 => 150 + 30, Bus b when ((double)b.RidersCount / (double)b.Capacity) > 0.90 => 150 - 40, Bus b => 150,

مطابقة الأنماط — كيان شاحنة التوصيل

 DeliveryTruck t when (t.Weight > 5000) => 200 + 100, DeliveryTruck t when (t.Weight < 3000) => 200 - 20, DeliveryTruck t => 200,

دمج جميع الكيانات

يسلط المثال أدناه الضوء على مزايا مطابقة الأنماط: يتم تجميع فروع النمط بالترتيب. كما يحذر المترجم من عدم إمكانية الوصول إلى الكود.


 public static int OccupancyTypeTollFare(Object vehicleType) => vehicleType switch { Car { PassengerCount: 0 } => 100 + 10, Car { PassengerCount: 1 } => 100, Car { PassengerCount: 2 } => 100 - 10, Car c => 100 - 20, Taxi { Fare: 0 } => 100 + 10, Taxi { Fare: 1 } => 100, Taxi { Fare: 2 } => 100 - 10, Taxi t => 100 - 20, Bus b when ((double)b.RidersCount / (double)b.Capacity) < 0.50 => 150 + 30, Bus b when ((double)b.RidersCount / (double)b.Capacity) > 0.90 => 150 - 40, Bus b => 150, DeliveryTruck t when (t.Weight > 5000) => 200 + 100, DeliveryTruck t when (t.Weight < 3000) => 200 - 20, DeliveryTruck t => 200, null => 0, { } => 0, };

اختبار البرنامج أعلاه

أمثلة للاختبار من وجهة نظر تطبيق وحدة التحكم. يوضح الكود أدناه كيفية استدعاء دالة مطابقة الأنماط أعلاه من الطريقة الرئيسية.


 var car1 = new Car{ PassengerCount=2}; var taxi1 = new Taxi { Fare = 0 }; var bus1 = new Bus { Capacity = 100, RidersCount = 30 }; var truck1 = new DeliveryTruck { Weight = 30000 }; Console.WriteLine($"The toll for a car is {OccupancyTypeTollFare(car1)}"); Console.WriteLine($"The toll for a taxi is {OccupancyTypeTollFare(taxi1)}"); Console.WriteLine($"The toll for a bus is {OccupancyTypeTollFare(bus1)}"); Console.WriteLine($"The toll for a truck is {OccupancyTypeTollFare(truck1)}");

مخرجات وحدة التحكم

 The toll for a car is 90 The toll for a taxi is 110 The toll for a bus is 180 The toll for a truck is 300


"يجعل مطابقة الأنماط الكود أكثر قابلية للقراءة ويقدم بديلاً لتقنيات التوجه نحو الكائنات عندما لا تتمكن من إضافة الكود إلى فئاتك."

نمط التصميم — Singleton

نمط تصميم عصابة الأربعة - يضمن أن فئة معينة لديها مثيل/كائن واحد فقط ونقطة وصول عالمية.


أهداف التعلم

  • كيفية البرمجة باستخدام نمط التصميم الفردي؟

ابدء

يتم استخدام فئات Singleton للتخلص من إنشاء أكثر من كائن واحد من فئة معينة.


 public class SingletonExample { private string Name { get; set; } = "Hello from singleton"; private static SingletonExample _instance; public static SingletonExample Instance { get { if (_instance == null) { _instance = new SingletonExample(); } return _instance; } } public SingletonExample() { } public string GetName() => Name; }

انفصال

  1. التكرار 1 _instance==null يعني أنه سيتم إنشاء المثيلات فقط.
  2. التكرار 2، كما هو الحال الآن _intance !=null لذا سيتم إرجاع المثيلات التي تم إنشاؤها مسبقًا.

الاختبار باستخدام تطبيق وحدة التحكم

دعنا نستدعي فئة singleton مرتين ونقوم بتعيين المثيل المُعاد إلى متغيرين مختلفين. أخيرًا، نتحقق مما إذا كان كلا الكائنين متساويين باستخدام الدالة Object.Equals.


 static void Main(string[] args) { var response = SingletonExample.Instance; Console.WriteLine(response); var response1 = SingletonExample.Instance; Console.WriteLine(response1); Console.WriteLine(Object.Equals(response1, response)); }
  • إذا تم إرجاع القيمة true، فهذا يعني أنه يتم إنتاج مثيل واحد في كل مرة.
  • إذا تم إرجاع القيمة false، فهذا يعني أن الفصل لا يتبع نمط singleton.

الناتج

يعود ناتج وحدة التحكم إلى القيمة true؛ تهانينا. لقد نجحت في تنفيذ نمط Singleton.



سلامة الخيط

تُعرف الفئة المذكورة أعلاه باسم فئة singleton، ولكنها ليست آمنة للخيوط حاليًا. في بيئة متعددة الخيوط، يمكن لخيطين الوصول إلى عبارة if (_instance == null) في نفس الوقت، وسوف ينتهي بنا الأمر بالحصول على مثيلات متعددة لفئة singleton.


إحدى الطرق للحصول على خيط أكثر أمانًا هي استخدام آلية القفل، والطريقة الأخرى هي إنشاء مثيل للقراءة فقط للحصول على نهج أنظف وأكثر كفاءة.

 public class ThreadSafeSingleton { private static readonly ThreadSafeSingleton _instance = new ThreadSafeSingleton(); public static ThreadSafeSingleton Instance { get { return _instance; } } public ThreadSafeSingleton() { } }

عينة من موقع Github

https://github.com/ssukhpinder/DesignPatterns

شكرا لك على القراءة!

تساعدني الرعايات على مواصلة صيانة وبناء مشاريع جديدة مثل هذه.


🙏 إذا كنت تستخدم Pay أو Noticed أو أيًا من مشاريعي الأخرى، فإن المساهمة الصغيرة ستعني الكثير. لا تكفي البرمجيات مفتوحة المصدر بمفردها لتغطية تكاليف المعيشة. آمل أن أتمكن بمساعدتك من مواصلة عملي بشكل مستدام، ولن أضطر إلى البحث عن وظيفة حقيقية 😛.

برمجة سي شارب🚀

شكرا لك على كونك جزءًا من مجتمع C#!

اشتري لي القهوة