
public class OrderBook
{
public Buyer Buyer { get; set; }
public Book Book { get; set; }
public double Price { get; set; }
public DateTime Date { get; set; }
(...)
public Order(Buyer buyer, Book book,
double price, DateTime date (...))
{
Buyer = buyer;
Date = date;
Product = product;
Price = price;
(...)
}
}
כפי שאתם יכולים לראות זה לא יהיה פשוט ליצור אובייקט של הזמנה חדשה עם כמות השדות האלה, במיוחד שחלק מהשדות הן אובייקטים בעצמם ובנוסף – מי יודע לאן האובייקט הזה יתפתח בעתיד?
האם נרצה להיות תלויים בבנאי הקלאסי? כך זה נראה ללא תבנית עיצוב:
Buyer buyer = _buyerRepository.GetById(buyerId);
Book book = _bookRepository.GetById(bookId);
Order order = new Order(buyer, book, price, date);
כך זה ייראה ע”י שימוש בתבנית Builder
OrderBook order = OrderBuilder.Init(buyerRepository, sellerRepository)
.SetBuyer(buyerId)
.SetBook(sellerId)
.SetDate()
.Build();
public class OrderBuilder
{
private OrderBook _order = new OrderBook();
private readonly IBuyerRepository _buyerRepository;
private readonly IBookRepository _bookRepository;
private OrderBuilder(IBuyerRepository buyerRepository,
IBookRepository bookRepository)
{
_buyerRepository = buyerRepository;
_bookRepository = bookRepository;
}
public static OrderBuilder Init(IBuyerRepository buyerRepository,
IBookRepository bookRepository)
{
return new OrderBuilder(buyerRepository, bookRepository);
}
public OrderBook Build() => _order;
public OrderBuilder SetBuyer(int buyerId)
{
_order.Buyer = _buyerRepository.GetById(buyerId);
return this;
}
public OrderBuilder SetBook(int bookId)
{
_order.Buyer = _bookRepository.GetById(bookId);
return this;
}
public OrderBuilder SetDate()
{
_order.PurchaseDate = DateTime.Now;
return this;
}
(...)
}
עם הBuilder תקבלו הרבה סדר וארגון בקוד לטווח הארוך.
מספר בנאים עבור מטרות דומות
בדוגמא הבאה אציג לכם דרך ליצור ספרים – פעם אחת ספר מודפס ובפעם השניה ספר דיגיטלי
בדוגמא הזו ההבדל היחיד בינהם יהיה פורמט הספר שיוגדר אוטומטי וישתנה כתלות בבנאי שתבחרו.
תחילה נממש את המחלקה Book:
public class Book
{
public Book(string name, string author, string language, string format, int numOfPages, string publisher, string distributor)
{
Name = name;
Author = author;
Language = language;
Format = format;
NumOfPages = numOfPages;
Publisher = publisher;
Distributor = distributor;
}
public string Name { get; set; }
public string Author { get; set; }
public string Language { get; set; }
public string Format { get; set; }
public int NumOfPages { get; set; }
public string Publisher { get; set; }
public string Distributor { get; set; }
public override string ToString()
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("{\n");
stringBuilder.Append($"\tName: {Name}\n");
stringBuilder.Append($"\tAuthor: {Author}\n");
stringBuilder.Append($"\tLanguage: {Language}\n");
stringBuilder.Append($"\tFormat: {Format}\n");
stringBuilder.Append($"\tNumOfPages: {NumOfPages}\n");
stringBuilder.Append($"\tPublisher: {Publisher}\n");
stringBuilder.Append($"\tDistributor: {Distributor}\n");
stringBuilder.Append("}");
return stringBuilder.ToString();
}
}
לאחר מכן נגדיר ממשק שממנו ירשו כל סוגי הבנאים ( יצירת ספר דיגיטלי, יצירת ספר מודפס ועוד.. )
public interface IBookBuilder
{
Book Build();
IBookBuilder SetName(string name);
IBookBuilder SetAuthor(string author);
IBookBuilder SetLanguage(string lang);
IBookBuilder SetFormat();
IBookBuilder SetNumOfPages(int pages);
IBookBuilder SetPublisher(string pub);
IBookBuilder SetDistributor(string dist);
}
וכעת היוצר של הספר הדיגיטלי:
public class EBook : IBookBuilder
{
Book _book = new Book();
public Book Build() => _book;
public IBookBuilder SetName(string name)
{
_book.Name = name;
return this;
}
public IBookBuilder SetAuthor(string author)
{
_book.Author = author;
return this;
}
public IBookBuilder SetLanguage(string lang)
{
_book.Language = lang;
return this;
}
public IBookBuilder SetFormat()
{
_book.Format = "Electronic";
return this;
}
public IBookBuilder SetNumOfPages(int pages)
{
_book.NumOfPages = pages;
return this;
}
public IBookBuilder SetPublisher(string pub)
{
_book.Publisher = pub;
return this;
}
public IBookBuilder SetDistributor(string dist)
{
_book.Distributor = dist;
return this;
}
}
כעת אותו הדבר עבור ספר מודפס:
public class PrintedBook : IBookBuilder
{
Book _book = new Book();
public Book Build() => _book;
public IBookBuilder SetName(string name)
{
_book.Name = name;
return this;
}
public IBookBuilder SetAuthor(string author)
{
_book.Author = author;
return this;
}
public IBookBuilder SetLanguage(string lang)
{
_book.Language = lang;
return this;
}
public IBookBuilder SetFormat()
{
_book.Format = "Printed";
return this;
}
public IBookBuilder SetNumOfPages(int pages)
{
_book.NumOfPages = pages;
return this;
}
public IBookBuilder SetPublisher(string pub)
{
_book.Publisher = pub;
return this;
}
public IBookBuilder SetDistributor(string dist)
{
_book.Distributor = dist;
return this;
}
}
לבסוף תוכנית שתפעיל את הקוד שיצרנו, כאשר ניתן לשים לב שהיא יוצרת את אותו הספר בדיוק עם אותם ארגומנטים וכפי שציינתי קודם ההבדל הוא הפורמט שהשתנה.
public class Program
{
const string BOOK_NAME = "Clean Code";
const string AUTHOR = "Robert Martin";
const string PUBLISHER = "Pearson Education (US)";
const string DISTRIBUTOR = "Pearson";
const int NUM_OF_PAGES = 464;
static void Main(string[] args)
{
EBook eBookBuilder = new EBook();
Book ebook = eBookBuilder.SetFormat()
.SetName(BOOK_NAME)
.SetAuthor(AUTHOR)
.SetPublisher(PUBLISHER)
.SetLanguage("English")
.SetNumOfPages(NUM_OF_PAGES)
.Build();
Console.WriteLine(ebook);
Console.WriteLine("\n---------------------------------------------\n");
PrintedBook pBookBuilder = new PrintedBook();
Book pbook = pBookBuilder.SetFormat()
.SetName(BOOK_NAME)
.SetAuthor(AUTHOR)
.SetPublisher(PUBLISHER)
.SetDistributor(DISTRIBUTOR)
.SetNumOfPages(NUM_OF_PAGES)
.Build();
Console.WriteLine(pbook);
}
}
כך נראה הפלט – באחד הפורמט דיגיטלי ובשני מודפס:
חדי העין ישימו לב שבאחד מהם לא כתובה השפה אנגלית וזה בדיוק עונה לנו על הצורך שלא תמיד נרצה לבנות הכל וחלק מהשדות הן לגמרי אופציונאליים.
הדוגמא זמינה גם בGitHub
סיכום
תבנית Buider היא פתרון מעולה במקרים שיש לך אובייקט עם קונסטרוקטור מרובה פרמטרים וכאשר חלק מהפרמטרים הם אובייקטים בעצמם.
קונסטרוקטור בעל פרמטרים אופציונליים מרובים הוא גם סיבה מצויינת להשתמש בתבנית הבנאי.
היי עמית,
דבר ראשון, תודה רבה! אשמח לעוד הסברים על סוגי התבניות השונים.
בהקשר להסבר הזה , עקבתי אחרי ההסבר וכתבתי אחד בעצמי, כשאני מנסה להגדיר בתוך המחלקה את האובייקט הפנימי (לדוגמתך Book _book = new Book();) הוא זורק שגיאה שהוא מצפה ממני להכניס פרמטרים..ואז אנחנו מאבדים את כל היתרונות של התבנית הזו, מכיר?
תודה רבה.