“Патерн Вівторка” #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) у інтерфейсі компоненту, тому можливо вам захочеться їх добавити. Одне хочу сказати – ніколи не зациклюєйтися на якихось прикладах і однотипних поясненнях. Ваші потреби можуть дещо відрізнятися від того що описано як дизайн патерн, але в той же час воно вам ідеально підходить, просто у вас якийсесь вироджене або ускладнене застосування.

Моя табличка Патернів
Developer's RoadMap To Success

2 коментарі

Залишити коментар до ufo_133 Скасувати коментар

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *