Trwają pracę nad projektem easyGALib. Ostatnio miałeś okazję czytać o zasadzie odwracania zależności, a teraz kolej na coś co wynika z jej stosowania poniekąd, czyli wzorzec projektowy – fabryka abstrakcyjna.
Idąc konsekwentnie do przodu w postanowieniu, jakim jest pisanie owego bloga i tworzenie projektu, cały czas staram się stosować omawiane w tekstach zasady. Wszystko po to, aby końcowy kod był jak najbardziej elastyczny i nie skończył się słowami „najlepiej byłoby to napisać od nowa”, którymi bardzo często kończą się nieprzemyślane przedsięwzięcia programistyczne. Tak więc idąc tą ścieżką, nadal tworzę abstrakcje, które będą sercem easyGALib, a dopiero w dalszym etapie zajmiemy się szczegółowymi implementacjami, bo na pewno pamiętasz z ostatniego posta, że to szczegółowa implementacja zależy od abstrakcji, a nigdy odwrotnie!
Zacząłem więc od utworzenia klasy pozwalającej na wywołanie działania biblioteki:
public class GAMain { private readonly IGeneticAlgorithmInput _input; private readonly IGAFactory _gaFactory; public GAMain(IGeneticAlgorithmInput input) { _input = input; _gaFactory = new GAFactory(); } public Chromosome Execute() { IGABase ga = _gaFactory.GetGA(_input.Parameters.CromosomeType); return null; } }
Co my tu mamy? Konstruktor przyjmujący na wejściu cokolwiek implementującego interfejs zawierający dane potrzebne do działania algorytmu. Nie wiem czy zauważyłeś, nastąpiła drobna zmiana nazwy od ostatniego posta 🙂 Zapisujemy sobie go z konstruktora do prywatnego pola, aby móc swobodnie korzystać z niego w całej klasie. Idąc dalej, wreszcie pojawia się fabryka występujące w tytule! Na co to komu?
Działając na algorytmach genetycznych będziemy operować na różnego rodzaju danych – od stringów, po inty i dlatego dla każdego z nich będziemy musieli mieć nieco inną implementację działania samego algorytmu. Chcemy mieć możliwość swobodnego dostarczania odpowiednich implementacji i dlatego właśnie pojawia się nam wzorzec projektowy – fabryka abstrakcyjna.
Fabryka abstrakcyjna
Fabryka abstrakcyjna to konstrukcyjny wzorzec projektowy, który ma za zadanie tworzyć obiekty z określonej rodziny, ot cała idea. Dlaczego w nazwie pojawia się słowo abstrakcyjna? Ano bo już mówiliśmy, że kodem powinna rządzić abstrakcja, więc najwięcej będziemy korzystać z takiego interfejsu:
interface IGAFactory { IGABase GetGA(ChromosomeType type); }
Na wejście podajemy typ chromosomu, a w zamian mamy dostać konkretną implementację algorytmu genetycznego. Jak to będzie zrobione? W warstwie abstrakcyjnej zupełnie nas to nie interesuje i to jest plus wykorzystania fabryki abstrakcyjnej – jej implementację możemy dowolnie zmieniać, gdy np. uznamy że inaczej będziemy wytwarzać algorytmy, a i tak nie będzie to miało wpływu na resztę kodu. Na teraz implementacja wygląda tak:
class GAFactory : IGAFactory { public IGABase GetGA(ChromosomeType type) { switch (type) { case ChromosomeType.StringChromosome: return GetGAString(); case ChromosomeType.CharChromosome: return GetGAChar(); case ChromosomeType.DoubleChromosome: return GetGADouble(); case ChromosomeType.IntChromosome: return GetGAInt(); default: return null; } } private IGAChar GetGAChar() { return new GAChar(); } private IGADouble GetGADouble() { return new GADouble(); } private IGAInt GetGAInt() { return new GAInt(); } private IGAString GetGAString() { return new GAString(); } }
Fabryka abstrakcyjna skupia się więc na dostarczeniu metod do wytwarzania obiektów z jednej rodziny, a nie przejmuje się w jaki sposób to zostanie zrobione. W kodzie na razie jawnie tworzę instancję GAFactory, jednak z czasem mogę zdecydować, że zastosuję wstrzykiwanie zależności, albo napiszę inną klasę, która będzie dostarczać implementacje algorytmów genetycznych i bez żadnego problemu będę ją w stanie podmienić!
Widać więc, że warto być konsekwentnym w stosowaniu abstrakcji, zamiast szczegółowych implementacji, bo zaprocentuje to w sytuacji, gdy uznamy że czas na zmiany. Jeżeli zaciekawił Cię sam temat fabryki, warto zaglądnąć do tego posta po więcej podobnych wzorców i inne przykłady.
Dodaj komentarz