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.