فهم خطة البناء للإنشاءات المعقدة في C#

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

وسواء كنت تدمجين موضوعا تشكيليا يتضمن عشرات الممتلكات الاختيارية، أو تضعين تقريرا مركبا، أو تضعين خطا متطورا للبيانات، فإن خطة البناء توفر نهجا منظما ومتقدما، وفي هذه المادة، سنستكشف النمط بعمق: مكوناتها الأساسية، وأمثلة عملية من نوع C#، واختلافات مثل البنيان المتناثر، ومتى تختارينها على أنماط خلقية أخرى.

ما هو "بائع البناء"؟

The Builder Pattern is a creational design pattern] that decouples the construction of a complex object from its final representation. instead of forcing a client to pass every parameter into a single constructor, the pattern lets you build the object piece by piece by piece, often through a series of method calls. The same builder” can be instructed by a director to create different representation.

والنمط مفيد بصفة خاصة عندما:

  • ويتطلب أي غرض العديد من المعايير الاختيارية أو المترابطة.
  • ويشمل التشييد خطوات متعددة قد يلزم القيام بها بموجب أمر محدد.
  • تريد إعادة استخدام نفس عملية البناء لخلق مختلف متغيرات الجسم.
  • ولا ينبغي أن تتعرض الحالة الداخلية للجسم إلا بعد أن يتم بناؤه بالكامل.

The concept was formalized by the Gang of Four in their landmark book ]Design Patterns: Elements of Reusable Object-Oriented Software and has since become a staple in C# development. It is widely used in frameworks like [FLT:

العناصر الأساسية لنهج البناء

وتضم خطة البناء أربعة مشاركين رئيسيين:

  • Product] - الجسم المعقّد قيد التشييد، وكثيراً ما يحتوي على أجزاء كثيرة يتعين تجميعها.
  • Builder] (الصف الداخلي أو المستقطع) - تعلن الخطوات اللازمة لبناء المنتج، عادةً كطرائق مثل ، ، ، .
  • Concrete Builder] - Implements the builder interface to construct and assemble the parts of the product. It keeps track of the product being built and provides a way to retrieve the terminated object.
  • ]Director - Orchestrates the building process by calling the builder’s steps in a specific order. The director knows the recipe but is independent of the concrete builder, allowing the same algorithm to produce different representations.

ويقوم العميل عادة بتفسير بناء الخرسانة، وينقله إلى المدير (أو يسمي البنّاء مباشرة بأسلوب مُتقن)، ثم يسترجع المنتج النهائي.

معرض العالم الحقيقي (ب): بناء بيت عُلم

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

فئة المنتجات

public class House
{
 public string Foundation { get; set; }
 public string Walls { get; set; }
 public string Roof { get; set; }
 public string Windows { get; set; }
 public string Doors { get; set; }
 public bool HasGarage { get; set; }
 public bool HasGarden { get; set; }

 public override string ToString()
 => $"House: {Walls}, {Roof}, {Doors}, {Windows}, Garage: {HasGarage}, Garden: {HasGarden}";
}

وجه البناء

public interface IHouseBuilder
{
 void BuildFoundation();
 void BuildWalls();
 void BuildRoof();
 void BuildWindows();
 void BuildDoors();
 void BuildGarage();
 void BuildGarden();
 House GetResult();
}

البناء المضمون

public class ConcreteHouseBuilder : IHouseBuilder
{
 private House _house = new House();

 public void BuildFoundation() => _house.Foundation = "Concrete slab";
 public void BuildWalls() => _house.Walls = "Brick walls";
 public void BuildRoof() => _house.Roof = "Gable roof";
 public void BuildWindows() => _house.Windows = "Double‑pane windows";
 public void BuildDoors() => _house.Doors = "Wooden doors";
 public void BuildGarage() => _house.HasGarage = true;
 public void BuildGarden() => _house.HasGarden = true;

 public House GetResult() => _house;

 // Allow reset to reuse the builder
 public void Reset() => _house = new House();
}

المدير

public class HouseDirector
{
 private IHouseBuilder _builder;

 public HouseDirector(IHouseBuilder builder) => _builder = builder;

 // Standard house construction steps
 public House ConstructStandardHouse()
 {
 _builder.Reset();
 _builder.BuildFoundation();
 _builder.BuildWalls();
 _builder.BuildRoof();
 _builder.BuildWindows();
 _builder.BuildDoors();
 return _builder.GetResult();
 }

 // House with garage
 public House ConstructHouseWithGarage()
 {
 _builder.Reset();
 _builder.BuildFoundation();
 _builder.BuildWalls();
 _builder.BuildRoof();
 _builder.BuildWindows();
 _builder.BuildDoors();
 _builder.BuildGarage();
 return _builder.GetResult();
 }
}

مدونة العملاء

var builder = new ConcreteHouseBuilder();
var director = new HouseDirector(builder);

House standardHouse = director.ConstructStandardHouse();
Console.WriteLine(standardHouse);
// Output: House: Brick walls, Gable roof, Wooden doors, Double‑pane windows, Garage: False, Garden: False

House houseWithGarage = director.ConstructHouseWithGarage();
Console.WriteLine(houseWithGarage);
// Output: House: Brick walls, Gable roof, Wooden doors, Double‑pane windows, Garage: True, Garden: False

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

الفرق في بناء البطاقات

وفي التطور الحديث في مجال الحرف جيم - 1، كثيرا ما يقترن جهاز البناء الكلاسيكي بواجهة ذات تأثير ] لتحسين إمكانية القراءة، بدلا من استخدام مدير، يعود المبني نفسه من كل خطوة، مما يسمح بتسلسل الأساليب، وهذا أمر شائع بصفة خاصة في نماذج مؤشرات الأداء (مثلا، ).

public class FluentHouseBuilder
{
 private House _house = new House();

 public FluentHouseBuilder WithFoundation(string type)
 {
 _house.Foundation = type;
 return this;
 }

 public FluentHouseBuilder WithWalls(string material)
 {
 _house.Walls = material;
 return this;
 }

 public FluentHouseBuilder WithRoof(string style)
 {
 _house.Roof = style;
 return this;
 }

 public FluentHouseBuilder WithWindows(string type)
 {
 _house.Windows = type;
 return this;
 }

 public FluentHouseBuilder WithDoors(string type)
 {
 _house.Doors = type;
 return this;
 }

 public FluentHouseBuilder AddGarage() { _house.HasGarage = true; return this; }
 public FluentHouseBuilder AddGarden() { _house.HasGarden = true; return this; }

 public House Build() => _house;
}

// Usage
House modernHouse = new FluentHouseBuilder()
 .WithFoundation("Concrete slab")
 .WithWalls("Glass panels")
 .WithRoof("Flat roof")
 .WithWindows("Floor‑to‑ceiling")
 .WithDoors("Sliding glass")
 .AddGarage()
 .Build();

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

متى تستخدمين جهاز البناء

خطة البناء ليست دائماً أفضل خيار

  • Objects have many optional fields or complex initialization.] A constructor with 10+ parameters becomes unwieldy and error — The builder lets you set only what you need.
  • Construction involves a multi‐step process.] E.g., building a report that requires fetching data, formatting, and add headers/ feeters.
  • You need to create different representations of the same object.] The same builder interface can be implemented by multiple concrete builders (e.g., vs. ).
  • You want to enforce a particular construction order] without exposing the object under construction. The director can enforce that is called before .

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

Builder vs. Other Creational Patterns

البناء ضد طريقة المصانع

ويستخدم نمط [FLT:] الشكل الناجع عندما لا يمكن للفئة أن تتوقع نوع الأشياء التي يجب أن تخلقها، ويسمح بالبدء في إجراء عمليات النقل إلى طبقات فرعية، ويعود المصنع عادة إلى جسم كامل في مكالمة واحدة، بينما يقوم أحد البنين ببناء خط الهدف بخطوة، ويستخدم مصنعا عندما تحتاج إلى تحديد أي صف محدد يُتخذ خطوات فورية؛ ويستخدم جهاز البناء عندما يتعلق الجسم باختياري.

Builder vs. Abstract Factory

]Abstract Factory] provides an interface for creating families of related (or dependent) objects without specifying their concrete classes. It is similar to a group of factory methods. A builder, in contrast, focuses on constructing a single complex object. Abstract Factory often returns a finished product immediately, while a builder returns the object only after you’ve called the final build.

وفي الممارسة العملية، يمكن الجمع بين الاثنين: يمكن استخدام مصنع للجرد لإنشاء البنّاء نفسه (مثلاً، [(FLT:16])، أو يمكن للبنّاء استخدام مصنع للخلاصات لخلق أجزاء فردية من المنتج.

حالات الاستخدام المسبق والتغيرات

بناء جيلي للأجسام القابلة للتداول

وعند العمل مع الأجسام غير القابلة للتداول (مثل السجلات)، يمكن للبنّاء أن يتراكموا في الدولة ثم يبنوا الجسم غير القابل للاشتعال في طريقة ، وهذا أمر شائع في مكتبات مثل ] التكثيف الوافي ] أو أثناء تشكيل ]

public record ProductConfiguration
{
 public string Name { get; init; }
 public decimal Price { get; init; }
 public int Stock { get; init; }
 public bool IsAvailable { get; init; }
}

public class ProductConfigurationBuilder
{
 private string _name = "Default";
 private decimal _price;
 private int _stock;
 private bool _isAvailable;

 public ProductConfigurationBuilder WithName(string name) { _name = name; return this; }
 public ProductConfigurationBuilder WithPrice(decimal price) { _price = price; return this; }
 public ProductConfigurationBuilder WithStock(int stock) { _stock = stock; return this; }
 public ProductConfigurationBuilder SetAvailability(bool available) { _isAvailable = available; return this; }

 public ProductConfiguration Build()
 => new ProductConfiguration
 {
 Name = _name,
 Price = _price,
 Stock = _stock,
 IsAvailable = _isAvailable
 };
}

البناء بالحقن المعال

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

بناء العجلات (شخير ديولوجي)

وتحتاج بعض الأشياء إلى بناء المنتج في خطوات إلزامية وتسلسلية، وفي هذه الحالات، يمكن أن تخلق بناة " متدرجة " لا تعرض إلا الخطوة المسموح بها التالية، وهذا نوع من أجهزة الدولة ] داخل أحد البنايات، تستخدم في كثير من الأحيان في بناء الاستفسارات أو المكالمات الهاتفية، وعلى سبيل المثال، قد يرغمك أحد مبنيي طلبات شركة HTTP على تحديد العنوان.

أفضل الممارسات والخيوط المشتركة

  • Keep the builder focused.] A builder should construct one kind of product. If you need different product families, consider separate builders or an abstract factory.
  • Provide sensible defaults.] Not every step has to be called, The product should have reasonable defaults for optional parts.
  • Validate the final product in the ] method.] instead of check validity after each step, validate once at the end. Ref exception if the product is not in a valid state.
  • Consider immutability.] Once built, the product should typically be immutable or have a restricted interface. This prevents accidental modifications after construction.
  • تجنباً لكشف المنتج أثناء البناء.] إبقاء المنتج سراً داخل البناية حتى يُدعى، وهذا يحول دون استخدام العملاء جسماً غير كامل.
  • Prefer the fluent method for modern C#.] Fluent builders are more intuitive to use and reduce the need for a separate director class.

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

الموارد الخارجية

لتعميق فهمك لشرطة البناء والأنماط التصميمية عموما، استكشاف هذه المراجع الموثوقة:

خاتمة

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

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