Wzorce projektowe – wzorzec fabryka.
Współcześnie występuje wiele reguł, wzorców i zasad programowania, którymi kierują się obecni programiści. Żeby ich kod był zrozumiały i jasny dla innych oraz wydajny posługują się oprócz zasad SOLID, tzw. wzorcami projektowymi. Wzorce projektowe to zbiór najlepszych praktyk z gotowymi rozwiązaniami dla wybranych problemów napotykanych w trakcie projektowania rozwiązań zorientowanych obiektowo. Wzorce projektowe składają się przede wszystkim z: nazwy, opisu problemu, który rozwiązuje rozwiązania i konsekwencji jego użytkowania. Jest dużo wzorców projektowych jednym z nich jest wzorzec fabryka. W tym artykule opisze ten wzorzec oraz przedstawię przykład kodu programu przed zastosowaniem tego wzorca jak i po.
Wstęp
Wzorzec fabryka to jeden z częściej wykorzystywanych wzorców projektowych. Wzorzec ten występuje w dwóch wariantach: metoda fabrykująca (ang. Factory Method) i fabryka abstrakcyjna (ang. Abstract Factory). Z pewnością celem zastosowania wzorca jest dostarczenie interfejsu do tworzenia rodzin powiązanych lub wzajemnie zależnych obiektów bez definiowania ich konkretnych klas. Umożliwia jednemu obiektowi tworzenie różnych, powiązanych ze sobą, reprezentacji pod obiektów określając ich typy podczas działania programu. W związku z tym programista zyskuje abstrakcyjną warstwę, odpowiedzialną za tworzenie instancji obiektów w jakiś sposób powiązanych wspólnym interfejsem. Uzyskujemy wtedy kod, który jest bardziej skalowalny, łatwiejszy na rozbudowę. Wzorzec ten spełnia zasadę odwrócenia zależności oraz zasadę otwarte/zamknięte, otwarte na rozbudowę i zamknięte na modyfikację.
Oprócz wymienionych wyżej wariantów fabryk istnieje również inna odmiana fabryki. Jest nią prosta fabryka (ang. Simple Factory), która nie jest pełnoprawnym wzorcem. Z pewnością mnogość odmian sprawia, że są one często ze sobą mylone i błędnie opisywane. Wzorzec fabryka należy do grupy wzorców skatalogowanych przez Gang czworga. Poniżej przedstawię przykład dla wzorca fabryka abstrakcyjna, opisze jego zalety i wady.
Zastosowanie
Niewątpliwie wzorzec fabryka abstrakcyjna ma zastosowanie w wielu aplikacjach np. rozpatrzmy aplikację kliencką, która łączy się ze zdalnym serwerem. Celem projektanta takiej aplikacji jest z pewnością to, aby była ona przenośna. Jednym z rozwiązań takiego problemu jest stworzenie fabryki, która będzie tworzyła odpowiednie obiekty w zależności od tego na jakiej platformie się znajduje. Innym zastosowaniem wzorca fabryka jest stworzenie sieci pizzerii i aplikacji do zamawiania pizzy, gdzie będzie można dodawać nowe pizzę oraz składniki.
Przykład
Idealny przykład do użycia fabryki to kod, który wygląda mniej więcej tak:
IManageArea manageArea;
Employee employee = employeeService.Load("Managment", 42);
if (employee.isManager)
{
manageArea = new GlobalManageArea();
}
else
{
manageArea = new LocalManageArea();
}
manageArea.FireEmployees(1000);
// itd.
Kod 1. Kod programu bez użycia wzorca
Pomijając sensowność kodu, widać tutaj logikę, od której zależy utworzenie instancji klasy. Z pewnością całość powinna zostać zhermetyzowana wewnątrz fabryki.
Spójrzmy na przykładowy kod z użyciem wzorca fabryka:
// kształty
interface IShape { }
class Square : IShape { }
class Triangle : IShape { }
// liczby
interface INumber { }
class RealNumber : INumber { }
class ComplexNumber : INumber { }
// fabryka abstrakcyjna rodziny obiektów
abstract class MathTestFactory
{
public abstract IShape CreateShape();
public abstract INumber CreateNumber();
}
// konkretna fabryka rodziny obiektów
class PrimarySchoolTestFactory : MathTestFactory
{
public override IShape CreateShape()
{
return new Square();
}
public override INumber CreateNumber()
{
return new RealNumber();
}
}
// konkretna fabryka rodziny obiektów
class HighSchoolTestFactory : MathTestFactory
{
public override IShape CreateShape()
{
return new Triangle();
}
public override INumber CreateNumber()
{
return new ComplexNumber();
}
}
// klasa klienta (kontekst wykonania fabryki)
class MathTest
{
private MathTestFactory mathTestFactory;
public MathTest(MathTestFactory mathTestFactory)
{
this.mathTestFactory = mathTestFactory;
}
public void GenerateTest()
{
var shape = this.mathTestFactory.CreateShape();
var number = this.mathTestFactory.CreateNumber();
System.Console.WriteLine("Test wygenerowany");
}
}
static void Main(string[] args)
{
MathTest mathTest;
mathTest = new MathTest(new PrimarySchoolTestFactory());
mathTest.GenerateTest(); // łatwy test przy użyciu PrimarySchoolTestFactory()
mathTest = new MathTest(new HighSchoolTestFactory());
mathTest.GenerateTest(); // trudny test przy użyciu PrimarySchoolTestFactory()
}
Kod 2. Kod programu z użyciem wzorca
Klasa MathTestFactory jest fabryką abstrakcyjną definiującą rodzinę obiektów. Za pomocą kompozycji definiujemy, że obydwa elementy są w jakiś sposób od siebie zależne. Na potrzeby artykułu przyjęliśmy, że tworzą „test”. Następnie tworząc konkretne egzemplarze fabryk, możemy rodzinę obiektów stworzyć na wiele sposób i tak np. PrimarySchoolTestFactory tworzy rodzinę obiektów bardzo prostych (kwadrat i zbiór liczb rzeczywistych). Natomiast HighSchoolTestFactory tworzy rodzinę obiektów trudnych (trójkąt i zbiór liczb zespolonych).
Podsumowanie
Reasumując, wszelkie odmiany fabryki abstrakcyjnej potrafią przysporzyć wiele problemów nawet zaawansowanym programistom. Osobiście wzorzec fabryka uważam za najbardziej skomplikowany wzorzec gangu czworga. Jego niezwykła komplikacja polega na wielu możliwościach implementacji, jednakże wzorzec ten posiada szereg zalet: umożliwia tworzenie rodzin obiektów w jakiś sposób powiązanych ze sobą, zawsze występuje mechanizm dziedziczenia, mechanizm kompozycji oraz dedykowana klasa klienta. Minusem fabryki abstrakcyjnej jest mała skalowalność, dodając nowy produkt trzeba przerobić klasę fabryki abstrakcyjnej oraz konkretnych fabryk. Mam nadzieję, że ten artykuł choć trochę rozjaśnił Wam jak działa wzorzec fabryka i pokazał jak najlepiej go używać.