poniedziałek, 29 lutego 2016

Abstract Factory (Fabryka abstrakcyjna) - kreacyjny wzorzec projektowy

Fabryka abstrakcyjna (ang. Abstract Factory) – kreacyjny wzorzec projektowy, którego celem jest dostarczenie interfejsu do tworzenia różnych obiektów jednego typu (tej samej rodziny) bez specyfikowania ich konkretnych klas.
Dzięki fabryce otrzymujemy interfejs, służący do generowania różnych obiektów, które go spełniają.

Umożliwia jednemu obiektowi tworzenie różnych, powiązanych ze sobą, reprezentacji podobiektów określając ich typy podczas działania programu.
W fabryce abstrakcyjnej chodzi o tworzenie obiektów spokrewnionych ze sobą.

Problem, który pomaga rozwiązać Fabryka Abstrakcyjna dotyczy implantacji klas.
W momencie kiedy w programie klasy posiadają wiele różnych implementacji, fabryki mają za zadanie zgrupować różne implementacje obiektów i je stworzyć. Fabryki tworzą instancje klas, które pochodzą z jednej rodziny –  np. jedna fabryka odpowiada za stworzenie obiektów z polskich rodziny, a druga z chińskiej rodziny.

Wzorzec fabryka i wszelkie jej odmiany służą temu aby nie tworzyć konkretnych typów obiektu klasy tylko abstrakcyjne typy klas. Zamiast podawania słówka new tworzymy metodę, która zwraca abstrakcyjną instancję obiektu klasy.

Fabryka abstrakcyjna służy również temu by klasa, którą tworzymy była otwarta na rozbudowę, ale zamknięta na modyfikacje.

Uwaga
Wzorzec projektowy Fabryka Abstrakcyjna, podobnie jak Budowniczy, używa klas wytwórczych (w tym wypadku Fabryk) w celu ukrycia procesu tworzenia produktów.
Fabryka abstrakcyjna różni się od Budowniczego tym, że kładzie nacisk na tworzenie produktów z konkretnej rodziny, a Budowniczy kładzie nacisk na sposób tworzenia obiektów.

Struktura
Wzorzec zbudowany jest z kilku podstawowych klas:
  • Klient - Używa tylko interfejsów FabrykaAbstrakcyjna i AbstrakcyjnyProdukt
  • FabrykaAbstrakcyjna - Deklaruje interfejs dla produktów(operacji), które tworzą obiekty AbstrakcyjnyProdukt. Interfejs ten jest implementowany w KonkretnychFabrykach .
  • KonkretnaFabryka - Implementuje konkretne produkty(operacje) do tworzenia obiektów KonkretnyProdukt. Zawiera logikę.
  • AbstrakcyjnyProdukt - Deklaruje interfejs dla typu produktu
  • KonkretnyProdukt - Implementuje interfejs AbstrakcyjnyProdukt
Klasa Fabryka abstrakcyjna deklaruje abstrakcyjny interfejs umożliwiający tworzenie produktów. Interfejs ten jest implementowany w Fabrykach konkretnych, które odpowiedzialne są za tworzenie konkretnych produktów.
Każda fabryka konkretnego produktu posiada także metodę wytwórczą tego produktu.

Zawsze występuje mechanizm dziedziczenia i polimorfizm (do konkretnych fabryk) oraz mechanizm kompozycji (do powiązania klienta z określoną fabryką). Jej sednem jest to, że klient wykonuje jakieś operacje, ale nie wie z jakiej fabryk korzysta. Bez osobnej klasy klienta jest po prostu metodą fabrykującą. Bez zwracania rodziny obiektów (tylko pojedynczego typu) jest po prostu metodą fabrykującą.






Definicja abstrakcyjnej klasy:

abstract class AbstractFactory 
{
public abstract AbstractProductA CreateProductA();
public abstract AbstractProductB CreateProductB();
}

Definicje konkretnych fabryk:

class ConcreteFactory1 : AbstractFactory 
{
     public override AbstractProductA CreateProductA()
     {
         return new ProductA1();
     }

     public override AbstractProductB CreateProductB()
     {
         return new ProductB1();
     }
} 
class ConcreteFactory2 : AbstractFactory 
{
     public override AbstractProductA CreateProductA()
     {
         return new ProductA2();
     }

     public override AbstractProductB CreateProductB()
     {
         return new ProductB2();
     }
}

Definicje bazowe produktów:

abstract class AbstractProductA
{
}
abstract class AbstractProductB
{
}
 

Definicje konkretnych produktów:

class ProductA1 : AbstractProductA
{
}

class ProductA2 : AbstractProductA
{
}
class ProductB1 : AbstractProductB
{
}

class ProductB2 : AbstractProductB
{
}

Plusy
  • Umożliwia ukrycie szczegółów implementacyjnych klas reprezentujących konkretny produkt - klient widzi tylko interfejsy. 
  • Ukryciu ulegają także nazwy tych klas, co nie wymusza ich zapamiętywania i odizolowuje klienta od problemu określenia do której klasy należy obiekt.
  • Daje możliwość całkowitego ukrycia implementacji obiektów przed klientem. Klient widzi tylko interfejs i nie ma możliwości zajrzenia do kodu oraz to, że wymuszana jest spójność produktów.
  • Oddzielamy tworzenie obiektów (część zmienna) od kodu stałego (1 zasada projektanta aplikacji obiektowych)
  • Gromadzimy ZMIANY w jednym miejscu.
  • Program jest bardziej elastyczny dzięki uzależnieniu od abstrakcji, interfejsów.
  • Łatwiejsza wymiana całych rodzin produktów
  • Spójność produktów
  • Spójność produktów – w sytuacji, gdy pożądane jest, aby klasy produkty były z określonej rodziny, fabryka bardzo dobrze to zapewnia.

Minusy
  • Łamie zasadę open/closed principle (SOLID - elementy systemu takie, jak klasy, moduły, funkcje itd. powinny być otwarte na rozszerzenie, ale zamknięte na modyfikacje)
  • mała skalowalność - Dodając nowy produkt, trzeba zmodyfikować wiele innych klas np. konkretne fabryki, a dodając nowy typ do rodziny obiektów trzeba zmodyfikować klasę fabryki abstrakcyjnej. Czasem trzeba zmodyfikować także kod klasy klienta. Powinno to zostać zahermetyzowane wewnątrz fabryki.
  • Wszelkie odmiany fabryki abstrakcyjnej potrafią przysporzyć wiele problemów nawet zaawansowanym programistom. Wzorzec fabryki uważam za najbardziej skomplikowany wzorzec gangu czworga


Zastosowanie
Używaj gdy:
  • System powinien być niezależny od tego, jak jego produkty są tworzone, składane i reprezentowane
  • Chcemy ujawniać jedynie interfejsy dla biblioteki klas produktów
  • System powinien być konfigurowany przy użyciu jednej z wielu rodzin produktów
  • Tam, gdzie chcemy odciąć się od tworzenia instancji klas posługując się konkretnym typem. 
  • Instancja posiada skomplikowaną logikę tworzenia jej instancji albo np chcemy utworzyć instancję jakieś klasy, ale jej typ będzie zależny od dodatkowych parametrów
Przykłady stosowania:
  • interfejs użytkownika w zależności od systemu
  • obsługa różnych przeglądarek internetowych w serwisach WWW
  • połączenie bazy danych  
  • Obsługa generowania raportów w wielu formatach (HTML, PDF, XLS). Każdy format posiada swoją własną fabrykę, która tworzy elementy takie jak: nowa strona, tabela, a tabela komórki tabeli. Tak więc każdy format zawiera rodzinę zależnych od siebie elementów (nie można przecież łączyć użycia komórek w formacie XLS i PDF w raporcie HTML).

Przykład 1
Przyjmijmy że mamy dwie piekarnie. Jedna zlokalizowana jest w Krakowie druga w Warszawie. Obie pieką placki kruche i z owocami. Piekarnia w Krakowie piecze placek z jabłkami i śliwkami. Piekarnia w Warszawie piecze placki z wiśniami i truskawkami.

Przyjrzyjmy się temu opisowi od strony projektanta oprogramowania. Klient który zamawia placki ma możliwość zamówienia placka od dwóch różnych dostawców (piekarnie Kraków i Warszawa). W każdej z piekarń produkuje się placki o różnych składach a to co je łączy, to to, że są to placki typu Kruchy i Owocowy.

Te informacje są wystarczające do zbudowania wzorca Abstract Factory.

Definicja abstrakcyjnej klasy Abstract Factory:

public abstract class Bakery
{
     public abstract IFruitPie CreateFruitPie();
     public abstract ITartPie CreateTartPie();
}

 
Definicje konkretnych fabryk:


public class KrakowBakery : Bakery
{
     public override IFruitPie CreateFruitPie()
     {
          return new ApplePie(23.50);
     }

     public override ITartPie CreateTartPie()
     {
          return new PlumPie(28.40);
     }
}
public class WarsawBakery : Bakery
{
     public override IFruitPie CreateFruitPie()
     {
          return new CherryPie(33.68);
     }

     public override ITartPie CreateTartPie()
     {
          return new StrawberryPie(39.40);
     }
}


Interfejsy odpowiedzialne za typy produktów:


public interface IFruitPie
{
     string Description();
     double Price { get; }
}
public interface ITartPie
{
     string Description();
     double Price { get; }
}


Definicje konkretnych produktów (placków):


public class PlumPie : ITartPie
{
     private double _price;

     public PlumPie(double price)
     {
          this._price = price;
     }
    
     public string Description()
     {
          return "Tart Plum Pie";
     }

     public double Price
     {
          get { return this._price; }
     }
}

public class StrawberryPie : ITartPie
{
     private double _price;

     public StrawberryPie(double price)
     {
          this._price = price;
     }

     public string Description()
     {
          return "Tart Strawberry Pie";
     }

     public double Price
     {
          get { return this._price; }
     }
}
public class ApplePie : IFruitPie
{
     private double _price;

     public ApplePie(double price)
     {
          this._price = price;
     }

     public string Description()
     {
     return "Pie with fresh Apples";
     }

     public double Price
     {
     get { return _price; }
     }
}

public class CherryPie : IFruitPie
{
     private double _price;

     public CherryPie(double price)
     {
          this._price = price;
     }

     public string Description()
     {
          return "Cherry Pie with fresh Cherries";
      }

     public double Price
      {
          get { return _price; }
     }
}


Wywołanie:


class Program
{
     static void Main(string[] args)
     {
          Bakery bakery = new KrakowBakery();

          IFruitPie FruitPie = bakery.CreateFruitPie();
          ITartPie TartPie = bakery.CreateTartPie();

          bakery = new WarsawBakery();

          FruitPie = bakery.CreateFruitPie();
          TartPie = bakery.CreateTartPie();    
}

Przykład 2


Code example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MySample
{
    public interface IPerson
    {
        string Name { get; set; }
        string Type { get; set; }
    }

    public class Employee : IPerson
    {
        private string _Name;
        public string Name { get { return _Name; } set { _Name = value; } }
        public string Type { get { return "Employee"; } set { } }
    }

    public class Customer : IPerson
    {
        private string _Name;
        public string Name { get { return _Name; } set { _Name = value; } }
        public string Type { get { return "Customer"; } set { } }
    }

    public interface IPersonAbstractFactory
    {
        IPerson GetPerson();
        IPerson GetPerson(string name);
    }

    public class EmployeeFactory : IPersonAbstractFactory
    {
        public IPerson GetPerson()
        {
            return new Employee() { Name = "UnkownEmployee" };
        }

        public IPerson GetPerson(string name)
        {
            return new Employee() { Name = name };
        }
    }

    public class CustomerFactory : IPersonAbstractFactory
    {
        public IPerson GetPerson()
        {
            return new Customer() { Name = "UnkownCustomer" };
        }

        public IPerson GetPerson(string name)
        {
            return new Customer() { Name = name };
        }
    }

    public class MySample
    {
        public IPerson Person1;
        public IPerson Person2;

        public MySample()
        {
            IPersonAbstractFactory EmployeeFactory = new EmployeeFactory();
            Person1 = EmployeeFactory.GetPerson();
            Person2 = EmployeeFactory.GetPerson("Employee2");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MySample m = new MySample();
            Console.WriteLine(m.Person1.Name + " of " + m.Person1.Type);
            Console.WriteLine(m.Person2.Name + " of " + m.Person2.Type);
            Console.ReadLine();
        }
    }
}


Inne przykłady

Rozpatrzmy aplikację kliencką, która łączy się ze zdalnym serwerem. Celem projektanta takiej aplikacji jest to, aby była ona przenośna. Jednym z rozwiązań takiego problemu jest stworzenie fabryki, która będzie tworzyła odpowiednie obiekty w zależności od tego na jakiej platformie się znajduje.
Inne przykłady:















http://brasil.cel.agh.edu.pl/~09sbfraczek/fabryka-abstrakcyjna,1,28.html
http://www.algorytm.org/wzorce-projektowe/fabryka-abstrakcyjna-abstract-factory.html
---------------------------------------------------------------------------------------------------

Powiązania z innymi wzorcami
  • Często implementowana za pomocą metod wytwórczych, bądź prototypu.
  • Klasy Fabryki abstrakcyjnej są często zaimplementowane przy użyciu metody fabrykującej, ale mogą być też zaimplementowane przy użyciu prototypu. 
  • Wzorce Fabryka abstrakcyjna(Abstract Factory), Budowniczy(Builder) i Prototyp(Prototype) mogą być zaimplementowane jako Singletony
  • Często implementacja Budowniczego(Buildera) jest połączona z Fabryką abstrakcyjną(Abstract factory), żeby zachować elastyczność i nie tworzyć typów klas konkretnych.



Materiały:
https://pl.wikipedia.org/wiki/Fabryka_abstrakcyjna_%28wzorzec_projektowy%29
https://dotnetpage.wordpress.com/2012/10/22/fabryka-abstrakcyjna-wzorzec-konstrukcyjny/
http://patryknet.blogspot.com/2010/03/abstract-factory-pattern.html
http://tomaszjarzynski.pl/fabryka-abstrakcyjna-wzorzec-projektowy-abstract-factory/
https://www.p-programowanie.pl/wzorce-projektowe/fabryka-abstrakcyjna/
http://krzysztofjelonek.net/wzorce-projektowe-fabryki/
http://tomaszjarzynski.pl/fabryka-abstrakcyjna-wzorzec-projektowy-abstract-factory/
http://devman.pl/pl/techniki/wzorce-projektowe-3-fabryka-abstrakcyjnaabstract-factory-rodzaje/

http://www.tutorialspoint.com/design_pattern/abstract_factory_pattern.htm
http://www.oodesign.com/abstract-factory-pattern.html
http://www.dofactory.com/net/abstract-factory-design-pattern
http://www.dotnet-tricks.com/Tutorial/designpatterns/4EJN020613-Abstract-Factory-Design-Pattern---C
http://www.programcreek.com/2013/02/java-design-pattern-abstract-factory/
http://www.studytrails.com/java/design-patterns/abstract_factory_pattern.jsp
http://balaiotecnologico.blogspot.com/2009/07/design-patterns-com-delphi-abstract.html
http://howtodoinjava.com/design-patterns/creational/abstract-factory-pattern-in-java/
http://www.ritambhara.in/abstract-factory-design-pattern/
https://dzone.com/articles/design-patterns-abstract-factory
http://mahedee.net/abstract-factory-pattern/
http://www.codeexcursion.com/post/2012/abstract-factory-pattern.html
http://www.javatpoint.com/abstract-factory-pattern
http://blog.bekijkhet.com/2012/05/c-abstract-factory-pattern-combined.html
http://www.codeproject.com/Articles/185349/Abstract-Factory-Design-Pattern
https://cumps.be/nl/blog/read/design-patterns-abstract-factory-pattern


Brak komentarzy:

Prześlij komentarz