Fluent builder (шаблон проєктування)
Fluent builder — твірний шаблон проєктування, який спрощує процес створення об'єктів.
Мотивація ред.
Спростити процес створення важких об'єктів. Розв'язує проблему перевантажених конструкторів, а також проблему великої кількості аргументів в конструкторах.
Опис ред.
Нехай дано клас User
public class User
{
// FIELDS
private string name;
private string surname;
private int age;
private bool isMarried;
// CONSTRUCTORS
public User()
{
this.name = string.Empty;
this.surname = string.Empty;
this.age = 18;
this.isMarried = false;
}
public User(string name, string surname, int age, bool isMarried)
{
this.name = name;
this.surname = surname;
this.age = age > 18 ? age : 18;
this.isMarried = isMarried;
}
}
Його конструктор приймає чотири аргументи, що вже здається надлишковістю. Крім того кількість полів та аргументів може бути значно більшою, а для полів можуть бути присутні різноманітні перевірки.
Додамо клас, який буде відповідати за побудову нашого об'єкта
public class UserBuilder
{
// FIELDS
private User user;
// CONSTRUCTORS
public UserBuilder()
{
user = new User();
}
// METHODS
public UserBuilder SetName(string name)
{
user.name = name;
return this;
}
public UserBuilder SetSurname(string surname)
{
user.surname = surname;
return this;
}
public UserBuilder SetAge(int age)
{
user.age = age > 18 ? age : 18;
return this;
}
public UserBuilder SetIsMarried(bool isMerried)
{
user.isMarried = isMerried;
return this;
}
// BUILDING
public User Build()
{
return user;
}
}
Цей клас вміє лише будувати нашого User'a. Важливо відмітити, що кожний Set метод повертає this, тобто посилання на об'єкт будівельника. Це дозволить нам використати Fluent interface.
User user = new User.UserBuilder()
.SetName("John")
.SetSurname("Doe")
.Build();
Звісно це не є обов'язковою вимогою шаблону.
Переваги та недоліки ред.
Переваги ред.
- спрощує процес створення об'єкта
- спрощує конструктор об'єкта
- спрощує код
Недоліки ред.
- додає клас будівельник, для складних об'єктів
- не завжди будівельник має доступ до полів об'єкта. В деяких випадках, аби досягти цього, варто порушити інкапсуляцію, що не завжди є прийнятним рішенням
Зв'язок з іншими патернами ред.
- Будівник та Fluent Builder використовують з однаковою метою — боротьба з анти-шаблоном "телескопічний конструктор". Але варто розуміти, що Будівник надає інтерфейс для реалізації алгоритмів побудови складних об'єктів. Користувач працює із об'єктами-спадкоємцями через цей інтерфейс доступу. Fluent Builder надає користувачеві методи ініціалізації полів.
Реалізація ред.
C# ред.
namespace FluentBuilder
{
public class User
{
// FIELDS
private string name;
private string surname;
private int age;
private bool isMarried;
// CONSTRUCTORS
public User()
{
this.name = string.Empty;
this.surname = string.Empty;
this.age = 18;
this.isMarried = false;
}
public User(string name, string surname, int age, bool isMarried)
{
this.name = name;
this.surname = surname;
this.age = age > 18 ? age : 18;
this.isMarried = isMarried;
}
public static UserBuilder CreateBuilder()
{
return new UserBuilder();
}
// INNER CLASSES
public class UserBuilder
{
// FIELDS
private User user;
// CONSTRUCTORS
public UserBuilder()
{
user = new User();
}
// METHODS
public UserBuilder SetName(string name)
{
user.name = name;
return this;
}
public UserBuilder SetSurname(string surname)
{
user.surname = surname;
return this;
}
public UserBuilder SetAge(int age)
{
user.age = age > 18 ? age : 18;
return this;
}
public UserBuilder SetIsMarried(bool isMerried)
{
user.isMarried = isMerried;
return this;
}
// BUILDING
public User Build()
{
return user;
}
public static implicit operator User(UserBuilder builder)
{
return builder.user;
}
}
}
class Program
{
static void Main(string[] args)
{
// different ways of creating a builder
// user builder's constructor
User user1 = new User.UserBuilder()
.SetName("John")
.SetSurname("Doe")
.Build();
// user's static method
User user2 = User.CreateBuilder()
.SetName("John")
.SetSurname("Doe")
.Build();
// different ways of building user
// build method
User user3 = new User.UserBuilder()
.SetName("John")
.SetSurname("Doe")
.Build();
// conversion
User user4 = new User.UserBuilder()
.SetName("John")
.SetSurname("Doe");
System.Console.Read();
}
}
}
Див.також ред.
Джерела ред.
- A Fluent Builder in C# [Архівовано 15 березня 2019 у Wayback Machine.]
- FluentBuilder [Архівовано 30 березня 2019 у Wayback Machine.]
- C# Design Patterns – Fluent Builder Interface With Recursive Generics [Архівовано 30 березня 2019 у Wayback Machine.]