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