Fabryka klas (Factory Pattern)¶
Note
Fabryka klas umożliwia tworzenie obiektów różnych typów w zunifokowany sposób
Fabryka klas to wzorzec umożliwiający produkcję obiektów na żądanie, np. na podstawie napisu wczytywanego z linii poleceń lub z pliku konfiguracyjnego. Konstrukcja ta składa się co najmniej z 2 rodzajów elementów:
obiektów, które będziemy produkować (klasy dziedziczące po
Product)samej fabryki - klasy, która bedzie zwracała obiekty różnych typów, które jednak dziedziczą po wspólnej klasie bazowej
W klasie bazowej Product zdefiniujmy metodę product_feature() wspólną dla wszystkich klas potomnych,
czyli produktów:
1class Product: 2 def product_feature(self, *args): 3 raise NotImplemented
Produkty, to obiekty drukujące ustalone znaki na ekranie. Obiekty klasy Asterix drukują gwiazdki (*)
a obiekty Dash - kreski:
1class Asterix(Product): 2 3 def __init__(self, n): 4 self.__n = n 5 6 def product_feature(self, *args): 7 print("*" * self.__n) 8 9class Dash(Product): 10 11 def __init__(self, n): 12 self.__n = n 13 14 def product_feature(self, *args): 15 print("-" * self.__n)
Sama fabryka wywołuje odpowiednio konstruktory:
1class ProductFactory: 2 3 def produce(self, what, params): 4 if what == '*': return Asterix(params) 5 elif what == '-': return Dash(params) 6 else: 7 print("unknown product") 8 return None
Rozwiązanie takie jest dość proste, ma jednak poważną wadę: instrukcja wielokrotnego wyboru w metodzie produce()
klasy ProductFactory uniemożliwia konfigurowanie działania fabryki. Aby dodać nowy rodzaj produktu do fabryki,
musimy zmodyfikować kod metody produce(). Zalecanym rozwiązaniem jest wykorzystanie
wzorca Dyspozytor. Aby poprawić fabrykę, należy dodać jeszcze jedną hierarchię klas - specjalne obiekty, które
tworzą nasze produkty. W poniższym przykładzie dziedziczą one po klasie bazowej ObjectMaker.
W całym programie potrzebujemy zazwyczaj wiele produktów, a dla każdego z nich definiujemy odpowiedni ObjectMaker.
Fabryka klas natomiast jest tylko jedna. W poniższym przykładzie mamy dwa produkty: Dash oraz Asterix,
dla których definiujemy odpowiednio DashMaker oraz AsterixMaker:
1class ObjectMaker: 2 def make(self, *args): 3 raise NotImplemented 4 5class AsterixMaker(ObjectMaker): 6 7 def make(self, *args): 8 print("AsterixMaker called", file=stderr) 9 if len(args) > 0 : return Asterix(args[0]) 10 else: return Asterix(1) 11 12 def __str__(self): return "AsterixMaker" 13 14class DashMaker(ObjectMaker): 15 16 def make(self, *args): 17 print("DashMaker called", file=stderr) 18 if len(args) > 0: return Dash(args[0]) 19 else: return Dash(1) 20 21 def __str__(self): return "DashMaker"
Teraz fabryka klas może zarejestrować ObjectMaker dla odpowiadającego mu klucza aby powiązać z tymże kluczem
wytwarzanie obiektów zadanego typu. Metoda produce() wykorzystuje teraz wzorzec Dyspozytor, w którego
implementacji wykorzystano słownik.
Konfiguracja fabryki następuje w programie głównym; należy tego dokonać jeszcze przed wyprodukowaniem jakiegokolwiek
produktu. W tym celu użyjmy znaku * aby produkować gwiazdki (wiążąc go z AsterixMaker)
oraz - aby produkować kreski.
1if __name__ == "__main__": 2 3 factory = ProductFactory() 4 factory.register_product("-", DashMaker()) 5 factory.register_product("*", AsterixMaker()) 6 7 products = [] 8 for obj, cnt in [('*', 5), ('-', 2), ('-', 1), ('*', 5), ('-', 2)]: 9 products.append(factory.produce(obj,cnt)) 10 11 for p in products: p.product_feature()
Zauważ, że stworzyliśmy po jednym obiekcie każdego typu ObjectMaker-a, samych produktów (gwiazdek i kresek)
fabryka może wyprodukować dowolnie dużo.