Dzisiaj całkowicie o podstawach ale jednak warto przypomnieć sobie słownictwo. Każdy kojarzy chyba wzorzec factory. Oficjalnie wyróżnia się jednak kilka typów tego wzorca. Najpopularniejsze z nich to Factory method oraz Abstract Factory. W podręczniku do wzorców projektowych, znajdziemy je jako dwie osobne konstrukcje.
Czy to naprawdę tak ważne, aby znać różnice w nazewnictwie? Wzorce projektowe traktuję jako słownictwo. Zamiast wyjaśniać drugiej osobie, dokładnie co chcę napisać, używam wzorców projektowych, aby po prostu opisać daną konstrukcje. Traktuję więc podstawowe wzorce projektowe jako czyste słownictwo bo chyba większość programistów używa każdego dnia Template Method, ale niestety nie każdy jest tego świadomy.
Wracając do głównego tematu… Istnieje wiele konstrukcji, które można nazywać fabryką. Pierwszą z nich jest choćby następująca delegata:
Func<IDataPointReader> readerFactory = ()=>new ConcreteImplementation();
Kolejną konstrukcją może być statyczna metoda np.:
class StaticFactory { public static IDataPointReader CreatePointReader(ReaderType type) { switch(type) { case Type1: return new Type1PointReader(); case Type2: return new Type1PointReader(); default: return new AnotherPointReader(); } ] }
Jak prawie każde statyczne rozwiązanie jest to według mnie anty-wzorzec – nic innego jak globalny pojemnik, którego nie da się wstrzyknąć.
Można zmodyfikować trochę StaticFactory i używać niestatycznych metod. Ponadto można zdefiniować bazowy interfejs i potem po prostu wstrzykiwać implementację, tzn.:
interface IDataPointReaderFactory { IDataPointReader Create(ReaderType type); } class ProductionFactory: IDataPointReaderFactory { public IDataPointReader Create(ReaderType type) { //... } }
Kolejna fabryka to wspomniana w tytule Factory Method:
abstract class Product{} class ProductA:Product{} class ProductB : Product { } abstract class Factory { public abstract Product Create(); } class ConcreteFactoryA : Factory { public override Product Create() { return new ProductA(); } } class ConcreteFactoryB : Factory { public override Product Create() { return new ProductB(); } }
Innymi słowy, klasy pochodne decydują jaki obiekt ma zostać stworzony. Powyższy przykład jednak jest bardzo sztuczny. Bardziej podoba się np. ten znaleziony na Wikipedia:
public class MazeGame { public MazeGame() { Room room1 = MakeRoom(); Room room2 = MakeRoom(); room1.Connect(room2); this.AddRoom(room1); this.AddRoom(room2); } protected virtual Room MakeRoom() { return new OrdinaryRoom(); } } public class MagicMazeGame:MazeGame { protected override Room MakeRoom() { return new MagicRoom(); } }
Z przykładu widać wyraźnie, że decyzja o typie może zapaść na dowolnym poziomie dziedziczenia – nie koniecznie na tym w którym jest instancja wykorzystywana.
Następnym wzorcem jest Abstract Factory. Zacznijmy znów od przykładu:
abstract class FactoryBase { public abstract ProductABase CreateProductA(); public abstract ProductBBase CreateProductB(); } class ConcreteFactory1 : FactoryBase { public override ProductABase CreateProductA() { return new ConcreteProductA1(); } public override ProductBBase CreateProductB() { return new ConcreteProductB1(); } } class ConcreteFactory2 : FactoryBase { public override ProductABase CreateProductA() { return new ConcreteProductA2(); } public override ProductBBase CreateProductB() { return new ConcreteProductB2(); } }
Fabryka we wzorcu Abstract Factory jest odpowiedzialna za tworzenie kilka różnych typów w przeciwieństwie do Factory Method. Jak widać w powyższym kodzie, każda z fabryk skojarzona może być z różnymi obiektami w ramach tej samej grupy ( w tym przypadku są to produkty).
Podsumowując:
- Factory Method to pojedyncza metoda, tworząca ten sam typ obiektu. Tworzenie obiektów jednak może być oddelegowane do klas pochodnych. W praktyce, często tworzymy normalny obiekt biznesowy, w którym występujące po prostu metoda Create. Rzadko tworzy się specjalnie osobną klasę “Factory”, aby zaimplementować factory method.
- Abstract Factory jest odpowiedzialny za tworzenie obiektów należących do tej samej grupy, rodziny. Zwykle tworzy się osobne klasy, aby zaimplementować ten wzorzec. Warto wspomnieć, że różne frameworki IoC wspierają fabryki i automatycznie generują je w formie delegaty Func<T1,T2,…>
Przykład z ConcreteFactoryA i ConcreteFactoryB to jest przykład na abstract factory, a nie factory method. Zresztą sam w nim napisałeś “abstract class Factory” :). Zdecydowanie różni się on od przykładu z Wikipedii, który jest poprawnym przykładem dla factory method.
@Piotr:
Nie zgodze sie. Jest to factory method. W ksiazkach zwykle nazywaja klase Creator. Ale najwazniejsze to, ze jest tam wylacnzie jedna metoda tworzaca product. A nie grupa metod jak to w przypadku abstract factory
O ile pamiętam, przykład z labiryntem pochodzi z książki “Bandy czworga” 🙂
@Pawel:
Mozliwe, nie pamietam w sumie juz GOF. W kazdym razie kod wzielem z Wikipedi.
Właśnie problemem jest, że jest tylko jedna metoda – tworząca. Tym samym cała klasa jest fabryką. Gdybyś miał tam jakieś inne metody oraz tę tworzącą (jak w przykładzie z Maze) to jest factory method. Factory method są wykorzystywane na dwa sposoby. Tak jak tu: TimeSpan.FromSeconds(123), lub wywoływane przez template method. Klasa zawierająca tylko metody tworzące to fabryka. Oczywiście każdą metodę, która zwraca obiekt można nazwać factory method naciągając trochę terminologię.
@Piotr:
Polecam przeanalizowanie tego diagramu:
http://upload.wikimedia.org/wikipedia/commons/a/a3/FactoryMethod.svg
A czy mógłbyś pokazać kawałek kodu, który wykorzystuje te factory method? Przy okazji, to jest błąd w ConcreteFactoryB.
Zaintereoswanym skromnie proponuję lekturę: http://corey.quickshiftconsulting.com/1/post/2009/5/first-post.html oraz ew. http://www.codeproject.com/Articles/716413/Factory-Method-Pattern-vs-Abstract-Factory-Pattern
W tym przypadku bardziej skłaniam się ku opini pana Piotrka Peraka.
@Piotr:
Przyklad to ten z Maze. Tak jak napisalem w poscie, nie tworzy sie osobnej klasy tylko po to aby stworzyc w niej factory method. Zwykle jest to metoda skladowa jakeigos obiektu (biznesowego).
Blad poprawilem, faktycznie byla literowka tam.
@Markin,Piotr:
Pierwszy listing kodu jest implementacja 1 do 1 diagramu UML. Moze nazwa nieszczesna i powinienem nazwac to Creator co sugeruje bardziej, ze dana klasa nie jest odpowiedzialna wylacznie za tworzenie klas a posiada takze inna logike. Jak sama nazwa wskazuje factory method dotyczy metody a nie klasy. Kolejna, najwazniejsza chyba roznica to fakt, ze abstract factory dotyczy rodziny obiektow.
U mnie Factory Method i drugi bardzo często chodzący z nim w parze wzorzec (kto wie jaki?) w praktyce, a nie teorii 🙂
http://writesoft.wordpress.com/2014/05/18/wzorce-projektowe-w-testach-jednostkowych/