Artykuł

sxc.hu sxc.hu
lip 24 2013
0

Wzorzec projektowy metoda wytwórcza w C#

W świecie informatyki zaprogramować można wszystko. Ponadto każdy problem, czy zagadnienie ma tysiące potencjalnych rozwiązań, a wszystko zależy jak to się często mówi od punktu widzenia osoby aktualnie siedzącej przy klawiaturze. Niektóre problemy - zagadnienia powtarzają się na tyle często, że pewne mądre głowy opracowały gotowe sposoby rozwiązań takich problemów. W świecie programistów nazywa się takie właśnie rzeczy wzorcami projektowymi.

Kiedyś opisywałem już na łamach tego blogu wzorzec projektowy Model View Controller, który cieszy się ogromną popularnością wśród webmasterów. Dziś chciałbym zaprezentować wzorzec projektowy Metody wytwórczej (z ang. factory method), ułatwiający tworzenie nowych obiektów.

Przykłady zaprezentowane w niniejszym artykule, przygotowane zostały w języku C#. Nic nie stoi jednak na przeszkodzie, by wykorzystać je w dowolnym innym obiektowym rozwiązaniu.

Idea wzorca projektowego metody wytwórczej

Wzorzec projektowy metody wytwórczej, pozwala na tworzenie obiektów w inteligenty sposób. Właściwa logika która określa jakiego konkretnego typu ma być obiekt, jest ukryta wewnątrz pewnej specjalnej metody wytwórczej, do której przekazujemy typ elementu oraz ewentualne parametry konstruktora.

Przez typ należy rozumieć parametr dowolnego typu, który musi umieć obsługiwać metoda wytwórcza. Może to być np. pewien string, integer, czy też enum itp. Docelowe klasy na podstawie, których powstanie wyjściowy obiekt, powinny jednak implementować określony wspólny interfejs. Czasami zamiast interfejsu stosuje się również klasy abstrakcyjne.

Stosując typy abstrakcyjne/interfejsy oszczędzamy sobie sporo pracy i bardzo ułatwiamy cały proces. Użytkownik nie musi się martwić o zachowanie wyjściowego obiektu, bo dzięki z góry określonej konstrukcji dobrze będzie wiedział czego się po nim spodziewać.

Podsumowując więc powyższą teorię, mamy tutaj kilka elementów:

  • Interfejs/klasę abstrakcyjną, który opisuje zachowanie docelowych klas
  • Klasy o konkretnych implementacjach, które jednocześnie wykorzystując wspomniany wcześniej interfejs, zachowują się według pewnego określonego schematu
  • Metodę wytwórczą, która na podstawie określonego parametru generuje obiekt odpowiedniej klasy

Za chwilę zobaczymy jak to wszystko działa w praktyce.

Tworzymy interfejs

Nasz interfejs będzie bajecznie prosty. Posiadać będzie deklarację jednej metody, która odpowiedzialna będzie.. za wydawanie dźwięków określonych zwierząt:)

interface IAnimal
{
    void MakeSound();
}

Klasy dla interfejsu

Aby nasz interfejs nie czuł się samotny i nie potrzebny, dopiszemy kilka klas, które okrutnie go wykorzystają...

class Cat : IAnimal
{
    #region IAnimal Members
    public void MakeSound()
    {
        Console.WriteLine("Miauuuu!");
    }
    #endregion
}

class Dog : IAnimal
{
    #region IAnimal Members
    public void MakeSound()
    {
        Console.WriteLine("Hauuuu!");
    }
    #endregion
}

class Wolf : IAnimal
{
    #region IAnimal Members
    public void MakeSound()
    {
        Console.WriteLine("Auuuu!");
    }
    #endregion
}

Mamy tutaj jak widać niezły zwierzyniec. Wszystkie trzy nasze nowe klasy implementują ten sam interfejs (IAnimal) i wypełniają te samą metodę (MakeSound), ale oczywiście każda na swój zwierzęcy, charakterystyczny sposób.

Enumeracja dla zwierzątek

Za poradą użytkownika @rek, utworzymy enumerację, która pozwoli na łatwiejsze rozpoznawanie naszych zwierzątek i uniknięcie błędów podczas tworzenia kodu.

enum Animals
{
    Cat, Dog, Wolf
}

W przyszłości można z powodzeniem tutaj dodać nowe zwierzaki:)

Tworzymy metodę wytwórczą

Żeby teraz nasze zwierzątka nie poczuły się niepotrzebne, musimy stworzyć klasę która będzie zawierać naszą główną metodę wytwórczą:

static class AnimalFactory
{
    public static IAnimal CreateAnimalObject(Animals animalType)
    {
        IAnimal animal = null;
        switch (animalType)
        {
            case Animals.Cat:
                animal = new Cat();
                break;
            case Animals.Dog:
                animal = new Dog();
                break;
            case Animals.Wolf:
                animal = new Wolf();
                break;
            default:
                throw new ArgumentOutOfRangeException("animalType", "Nieznany rodzaj zwierzaka");
        }
        return animal;
    }
}

Stworzyliśmy piękną klasę z jeszcze lepszą metodą wytwórczą. Ponieważ metoda nie operuje na żadnych składowych klasy, uczyniliśmy ją statyczną. Do naszej metody, przekazujemy typ zwierzęcia (wykorzystujemy do tego wcześniej utworzoną enumerację). Najpierw zapisujemy deklarację obiektu, na początku będącą nullem. Potem w zależności od przekazanego typu zwierzęcia, tworzymy instancję określonej klasy i przypisujemy ją do naszego dotychczas pustego obiektu. Na końcu zwracamy referencję do utworzonego obiektu rozszerzającego wcześniej zadeklarowany interfejs.

Zwróćcie również uwagę na domyślne zachowanie switcha. Rzucamy w tym miejscu piękny wyjątek ArgumentOutOfRangeException.

Testowanie kodu

Do przetestowania naszego kodu, wykorzystamy najzwyczajniejszą w świecie aplikację konsolową:

class Program
{
    static void Main(string[] args)
    {
        IAnimal animal = AnimalFactory.CreateAnimalObject(Animals.Dog);
        animal.MakeSound();
    }
}

Nasz programik jak widać skomplikowany nie jest. Tworzymy obiekt klasy AnimalFactory, następnie korzystamy z metody wytwórczej CreateAnimalObject przekazując do niej odpowiedni typ zwierzaka. W efekcie dostajemy obiekt, który na pewno dziedziczy po interfejsie IAnimal. Na koniec, możemy wydać dźwięk z obiektu naszego zwierzaka. O tym jaki będzie to odgłos, sugeruję się już przekonać osobiście;-)

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus