“Патерн Вівторка” #16: Компонувальник (Composite)
|Чи ви коли небуть задумувалися чому так багато у цьому світі має деревовидну структуру? Адміністративний устрій країни для прикладу, або ж ваша організація. Топ менеджмент компанії може делегувати роботу робому менеджерам відділів, які відповідно делегують її до ваших прямих менеджерів, а ті в свою чергу дадуть вам якусь роботу. Або для прикладу, XML, має деревовидну структуру, мабуть тому що це найкращий спосіб зберегти дані, що можуть містити дані, які в свою чергу можуть мітити дані, які… Отже, припустимо що вам слід зкомпонувати якийсь складний документ із частин. Деякі застини вміють збирати дані (GatherData), базуючить на відповідній ID-шці. Деякі частини, просто утримують інші частини. Ми побудуємо XML кустарними засобами у цьому прикладі.
Компонувальник – це такий дизайн патерн, який дозволяє нам зберігати деревовидну структуру і працювати однаково із батьками та синами у дереві.
Розглянемо інтерфейс що визначає спільні вимоги до батьків і дітей (:
public interface IDocumentComponent
{
string GatherData();
void AddComponent(IDocumentComponent documentComponent);
}
А тепер реалізація одного із листків у нашому дереві – CustomerDocumentComponent, яка вміє збирати кусок XML, базуючись на ID-шці замовника.
public class CustomerDocumentComponent : IDocumentComponent
{
private int CustomerIdToGatherData { get; set; }
public CustomerDocumentComponent(int customerIdToGatherData)
{
CustomerIdToGatherData = customerIdToGatherData;
}
public string GatherData()
{
string customerData;
switch (CustomerIdToGatherData)
{
case 41:
customerData = "Andriy Buday";
break;
default:
customerData = "Someone else";
break;
}
return string.Format("<Customer>{0}</Customer>", customerData);
}
public void AddComponent(IDocumentComponent documentComponent)
{
Console.WriteLine("Cannot add to leaf...");
}
}
А тепер реалізація нелисткових компонентів нашого дерева. Зверніть увагу, що в методі GatherData, ми просто ітеруємо по всіх “отприсках” і викликаємо наш основний метод для збору даних.
public class DocumentComponent : IDocumentComponent
{
public string Name { get; private set; }
public List<IDocumentComponent> DocumentComponents { get; private set; }
public DocumentComponent(string name)
{
Name = name;
DocumentComponents = new List<IDocumentComponent>();
}
public string GatherData()
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine(string.Format("<{0}>", Name));
foreach (var documentComponent in DocumentComponents)
{
documentComponent.GatherData();
stringBuilder.AppendLine(documentComponent.GatherData());
}
stringBuilder.AppendLine(string.Format("</{0}>", Name));
return stringBuilder.ToString();
}
public void AddComponent(IDocumentComponent documentComponent)
{
DocumentComponents.Add(documentComponent);
}
}
Клеїмо частинки докупи:
var document = new DocumentComponent("ComposableDocument");
var headerDocumentSection = new HeaderDocumentComponent();
var body = new DocumentComponent("Body");
document.AddComponent(headerDocumentSection);
document.AddComponent(body);
var customerDocumentSection = new CustomerDocumentComponent(41);
var orders = new DocumentComponent("Orders");
var order0 = new OrderDocumentComponent(0);
var order1 = new OrderDocumentComponent(1);
orders.AddComponent(order0);
orders.AddComponent(order1);
body.AddComponent(customerDocumentSection);
body.AddComponent(orders);
string gatheredData = document.GatherData();
Console.WriteLine(gatheredData);
Вивід певно схожий на XML, принаймні я хотів щоб він був таким.
<ComposableDocument>
<Header><MessageTime>8:47:23</MessageTime></Header>
<Body>
<Customer>Andriy Buday</Customer>
<Orders>
<Order>Kindle;Book1;Book2</Order>
<Order>Phone;Cable;Headset</Order>
</Orders>
</Body>
</ComposableDocument>
Варто добавити, що GoF-хлопці пропонують також методи Remove(Component) та GetChild(int) у інтерфейсі компоненту, тому можливо вам захочеться їх добавити. Одне хочу сказати – ніколи не зациклюєйтися на якихось прикладах і однотипних поясненнях. Ваші потреби можуть дещо відрізнятися від того що описано як дизайн патерн, але в той же час воно вам ідеально підходить, просто у вас якийсесь вироджене або ускладнене застосування.
Позитивно:) Спасибо!
Та тобі дякую!