Wzorzec ten umożliwia organizację obiektów w strukturę drzewiastą jako związek całość - część. Dzięki temu klient traktuje w taki sam sposób pojedyncze obiekty jak i złożone struktury.
Co zyskujemy dzięki temu? W większości przypadków możemy zignorować różnicę pomiędzy agregatem obiektów a indywidualnymi obiektami.
Schemat UML tego wzorca przedstawia się następująco:
Klasy z których składa się ten wzorzec są następujące:
Component - definiuje interfejs dla wszystkich obiektów drzewa
Leaf - reprezentuje liść, czyli węzeł który nie posiada potomków (podobnie jak Composite impelementuje metody Add, Remove itp. jednak w jego przypadku są one zbędne w implementacji).
Composite - definiuje zachowanie obiektów posiadających dzieci i przechowuje do nich referencje.
Przejdźmy do przykładu:
public abstract class CourseComponent <T>
{
public virtual void Add(CourseComponent<T> component)
{
throw new NotSupportedException();
}
public virtual void Remove(CourseComponent<T> component)
{
throw new NotSupportedException();
}
public virtual CourseComponent<T> GetChildren(int i)
{
throw new NotSupportedException();
}
public virtual string GetDescription()
{
return "";
}
}
//Our Composite
public class Course<T> : CourseComponent<T>
{
public string CourseName { get; set; }
private List<CourseComponent<T>> list = new List<CourseComponent<T>>();
public override CourseComponent<T> GetChildren(int i)
{
return list[i];
}
public override void Add(CourseComponent<T> component)
{
list.Add(component);
}
public override void Remove(CourseComponent<T> component)
{
list.Remove(component);
}
public override string GetDescription()
{
string s = CourseName + Environment.NewLine;
foreach (var item in list)
{
s+=item.GetDescription() + Environment.NewLine;
}
return s;
}
}
//Our leaf
public class Student<T> : CourseComponent<T>
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public override string GetDescription()
{
return _firstName + " " + _lastName;
}
}
class Program
{
static void Main(string[] args)
{
Course<string> mathCourse = new Course<string>();
mathCourse.CourseName = "MathCourse";
Course<string> algebraCourse = new Course<string> { CourseName = "AlgebraCourse" };
mathCourse.Add(algebraCourse);
algebraCourse.Add(new Student<string> { FirstName = "Mateusz", LastName = "Kowalski" });
mathCourse.Add(new Student<string> { FirstName = "Jan", LastName = "Kowalski" });
Console.WriteLine(mathCourse.GetDescription());
}
}
Mamy tutaj prosty przykład wzorca, pokazującego kursy i studentów do nich należących. Dzięki takiemu wzorcowi zarówno studentów jak i kursy możemy traktować tak samo.
Co należy rozważyć podczas pracy z tym wzorcem projektowym?
Przede wszystkim należy się zastanowić czy ważna jest dla nas kolejność dodawania elementów. Jeżeli tak musimy stworzyć rozwiązanie które pozwoli nam w prosty sposób manipulować "miejscem" gdzie dodawany jest nowy element. Także należy wziąć pod uwagę to, czy struktura jest rozbudowana. Jeżeli tak, można stworzyć mechanizm caschingu, który pozwoli na przechowywanie wyników obliczeń.
Kiedy nie stosować Composite Pattern?
Przede wszystkim wtedy kiedy klient potrzebuje dostępu do pojedynczych obiektów, albo te obiekty będą udostępniały metody niedostępne dla reszty drzewa.
Brak komentarzy:
Prześlij komentarz