AbstractFactory - zbyt mało abstrakcji 8584 14

O temacie

Autor Wonski

Zaczęty 12.07.2016 roku

Wyświetleń 8584

Odpowiedzi 14

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer
Cześć,
mam problem w zrozumieniu wzorca projektowego: Fabryka Abstrakcyjna.
Może zanim przejdę do problemu, przedstawię diagram UML i implementacje tego diagramu za pomocą kodu:

Diagram (troche spory rozmiar)
Spoiler


Implementacja:
Spoiler

Pierwsza rodzina produktów wraz z interfejsem
Spoiler
// Product Family A
class AbstractProductA
{
public:
virtual void process() = 0;
};

class Product_A1 : public AbstractProductA
{
public:
virtual void process()
{
cout << "Product_A1 from Family_A" << endl;
}
};

class Product_A2 : public AbstractProductA
{
public:
virtual void process()
{
cout << "Product_A2 from Family_A" << endl;
}
};

druga rodzina produktów wraz z interfejsem
Spoiler
// Product Family B
class AbstractProductB
{
public:
virtual void process() = 0;
};

class Product_B1 : public AbstractProductB
{
public:
virtual void process()
{
cout << "Product_B1 from Family_B" << endl;
}
};

class Product_B2 : public AbstractProductB
{
public:
virtual void process()
{
cout << "Product_B2 from Family_B" << endl;
}
};

fabryki konkretne wraz z interfejsem
Spoiler
class AbstractFactory
{
public:
virtual AbstractProductA * CreateProductA() = 0;
virtual AbstractProductB * CreateProductB() = 0;
};

class ConcreteFactory1 : public AbstractFactory
{
public:
AbstractProductA * CreateProductA()
{
return new Product_A1;
};

AbstractProductB * CreateProductB()
{
return new Product_B1;
};
};

class ConcreteFactory2 : public AbstractFactory
{
public:
AbstractProductA * CreateProductA()
{
return new Product_A2;
}

AbstractProductB * CreateProductB()
{
return new Product_B2;
}
};

klient
Spoiler
//client
class FinalProduct
{
AbstractProductA * _product_A;
AbstractProductB * _product_B;

public:

void process()
{
this->_product_A->process();
this->_product_B->process();
}

void build(AbstractFactory & _helper)
{
this->_product_A = _helper.CreateProductA();
this->_product_B = _helper.CreateProductB();
}

};

A oto kod za pomoca ktorego testowalem ta implementacje:
Spoiler
int main()
{
FinalProduct c1;

ConcreteFactory1 _obj;

c1.build(_obj);
c1.process();

ConcreteFactory2 _obj2;

c1.build(_obj2);
c1.process();

system("PAUSE");
return 0;
}

Jak widzimy nic szczególnego. Ot prosta implementacja fabryki abstrakcyjnej.
Jednak mnie zastanawia to czy dobrze ukryłem implementacje fabryk konkretnych. Z diagramu wynika (ten sam co z książki od GOF), ze klient nie powinien widzieć implementacji fabryk konkretnych.

Jeżeli zasłaniam implementacje przed użytkownikiem końcowym, tzn umieszczam w klasie reprezentującej klienta specjalnego switcha opartego na typie wyliczeniowym (albo jakichkolwiek stałych), to mam niezgodność z diagramem bo klasa klient zna implementacje fabryk konkretnych, ale plus jest taki, ze nie zna ich użytkownik końcowy.

Jeżeli zasłaniam implementacje przed klasa reprezentującą klienta, tzn korzystam z interfejsu fabryki abstrakcyjnej, to niby jest zgodność z diagramem, ale odpowiedzialność za znajomość implementacji fabryk konkretnych spada na użytkownika końcowego, który musi przecież jakoś zainicjować interfejs klasy reprezentującej klienta ( co widać w kodzie testującym).

W tych dwóch przypadkach jedyne co jest niewidoczne to produkty konkretne.Czy nie powinno być jednak tak, ze niewidoczne zostają również implementacje fabryk konkretnych?
Przecież ten kod lamie zasadę mówiącą by uzależniać kod od abstrakcji a nie od klas konkretnych.
 

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Nie, czemu? Fabryka ma dostarczyć interfejs dla rodziny konkretnych obiektów. Do fabryki masz przekazywać konkretne obiekty, które mają interfejs danej fabryki. Bo skąd w innym wypadku fabryka miałaby wiedzieć, jakie produkty ma tworzyć?

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer

Wonski
Gry (themodders@telegram)

AbstractFactory - zbyt mało abstrakcji
#2 2016-07-12, 19:40(Ostatnia zmiana: 2016-07-12, 20:22)
Takie samo miałem odczucie gdy implementowałem Budowniczego (W sumie wzorce podobne, ale jednak to inny proces konstrukcji).
Tyle abstrakcji i na koniec i tak wszystkie klasy były widoczne dla użytkownika końcowego.

A czy nie można rozwiązać tego w bardziej dyskretny sposób... tzn ukryć przed użytkownikiem końcowym konieczność tworzenia instancji fabryk konkretnych?
Mam na myśli tworzenie instancji w switchu wewnątrz metody build klasy FinalProduct (taka jakby fabryka statyczna).
Na użytkownika końcowego spada tylko obowiązek znajomości typu enum poprzez którego dokonuje się konkretyzacja. Dodatkowo użytkownik nie musi dbać o prawidłowe zwalnianie pamięci zajętej przez fabryki konkretne (tak wiem, można przejść na inteligentne wskaźniki).

Jednak przez takie działanie tracimy na abstrakcji.
Dodając nowy produkt musimy nie tylko konfigurować nowa fabrykę konkretna ale i również klienta.
Co o tym sadzisz?
 

inż. Avallach

inż. Avallach

Administrator
posty7661
Propsy5239
NagrodyV
ProfesjaProgramista
  • Administrator
Nie rób switcha po enumie. Wybieraj typ do stworzenia na podstawie danych które będą wykorzystane w procesie konstrukcji. Enum jest tutaj bardzo sztucznym rozwiązaniem które zdecydowanie nie powinno trafić do kodu produkcyjnego. Skoro masz typ który wylicza wszystkie możliwe implementacje i musisz z góry podawać o którą chodzi, to nie ma w tym właściwie żadnej prawdziwej abstrakcji. Równie dobrze mógłbyś od razu zawołać docelowy konstruktor.

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer
Cytuj
Wybieraj typ do stworzenia na podstawie danych które będą wykorzystane w procesie konstrukcji
Masz na myśli budowniczego?


Av, miałem raczej na myśli coś w tym stylu:

enum  EnumConcreteFactory
{
CONCRETE_FACTORY_1,
CONCRETE_FACTORY_2
};

//client
class FinalProduct
{
AbstractProductA * _product_A;
AbstractProductB * _product_B;

public:
void process()
{
this->_product_A->process();
this->_product_B->process();
}

void build(AbstractFactory & _helper)
{
this->_product_A = _helper.CreateProductA();
this->_product_B = _helper.CreateProductB();
}

void build(EnumConcreteFactory type_factory_helper)
{
switch (type_factory_helper)
{
case CONCRETE_FACTORY_1:
ConcreteFactory1 * concrete_factory_1_obj = new ConcreteFactory1;
this->_product_A = concrete_factory_1_obj->CreateProductA();
this->_product_B = concrete_factory_1_obj->CreateProductB();
break;

case CONCRETE_FACTORY_2:
ConcreteFactory2 * concrete_factory_2_obj = new ConcreteFactory2;
this->_product_A = concrete_factory_2_obj->CreateProductA();
this->_product_B = concrete_factory_2_obj->CreateProductB();
break;

default:
                        // wlasny wyjatek
throw UnexpectedException();
}
}
};

Dzięki temu rozwiązaniu, mogę zyskać trochę po stronie użytkownika końcowego.
Zamiast tego:
FinalProduct c1;
ConcreteFactory1 _obj;
c1.build(_obj);
c1.process();

mam tylko to (użytkownik w ogóle nie widzi implementacji fabryk konkretnych):
FinalProduct c1;
c1.build(CONCRETE_FACTORY_1);
c1.process();



No tylko kosztem dodatkowej pracy przy dodawaniu nowych produktów.
Czy to dobre rozwiązanie?
 

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Nie, masz przekazywać obiekty. Obiekty, które mają wspólny interfejs i są do siebie podobne. Pomyśl o jakiejś pętli. Jak będziesz przekazywać enuma? Jako tablicę? Czy będzie to czytelniejsze? Co w przypadku, gdy będziesz chciał dodać kolejną fabrykę? Będziesz musiał zmienić implementację, dodając enuma, łamiąc przy tym zasadę zamknięte - otwarte. Nie wiem, może ten przykład będzie pomocny: https://github.com/Adanos/ZooFactory/blob/master/ZooFactory/Program.cs

Poza tym, dlaczego tak usilnie chcesz ukrywać klasy? Nie chodzi o to, by ukrywać klasy przed użytkownikiem, tylko rozwiązać problem. Abstrakcja tutaj to ułatwienie rozwiązania problemu i zwiększenie jego ogólności.

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer

Wonski
Gry (themodders@telegram)

AbstractFactory - zbyt mało abstrakcji
#6 2016-07-12, 23:35(Ostatnia zmiana: 2016-07-13, 01:21)
Dobra, a zerknij na to:
Spoiler

Pierwsza rodzina produktów wraz z abstrakcyjnym interfejsem:
Spoiler
class AbstractProductA
{
public:
virtual void execute() = 0;
};

class ProductA1 : public AbstractProductA
{
public:
virtual void execute() override { cout << "ProductA1" << endl; }
};

class ProductA2 : public AbstractProductA
{
public:
virtual void execute() override { cout << "ProductA2" << endl; }
};

class ProductA3 : public AbstractProductA
{
public:
virtual void execute() override { cout << "ProductA3" << endl; }
};

Druga rodzina produktów wraz z abstrakcyjnym interfejsem:
Spoiler
class AbstractProductB
{
public:
virtual void process() = 0;
};

class ProductB1 : public AbstractProductB
{
public:
virtual void process() override { cout << "ProductB1" << endl; }
};

class ProductB2 : public AbstractProductB
{
public:
virtual void process() override { cout << "ProductB2" << endl; }
};

class ProductB3 : public AbstractProductB
{
public:
virtual void process() override { cout << "ProductB3" << endl; }
};

Fabryki konkretne wraz z abstrakcyjnym interfejsem (inny pomysł):
Spoiler
class AbstractFactory
{
public:
virtual AbstractProductA * createProductA(string concrete_product_name_help) { return nullptr; }
virtual AbstractProductB * createProductB(string concrete_product_name_help) { return nullptr; }
};

class ConcreteProductAFactory : public AbstractFactory
{
public:
virtual AbstractProductA * createProductA(string concrete_product_name_help) override
{
if (concrete_product_name_help == "ProductA1")
return new ProductA1;

else if (concrete_product_name_help == "ProductA2")
return new ProductA2;

else if (concrete_product_name_help == "ProductA3")
return new ProductA3;

return nullptr;
}
};

class ConcreteProductBFactory : public AbstractFactory
{
virtual AbstractProductB * createProductB(string concrete_product_name_help) override
{
if (concrete_product_name_help == "ProductB1")
return new ProductB1;

else if (concrete_product_name_help == "ProductB2")
return new ProductB2;

else if (concrete_product_name_help == "ProductB3")
return new ProductB3;

return nullptr;
}
};

Budowniczy fabryk (odmiana klienta):
Spoiler
class FactoryProducer
{
public:
static AbstractFactory * CreateFactory(string concrete_product_name_help)
{
if (concrete_product_name_help == "ProductA")
return new ConcreteProductAFactory;

else if (concrete_product_name_help == "ProductB")
return new ConcreteProductBFactory;

return nullptr;
}
};


Kod testujący:
Spoiler
AbstractFactory * abstract_factory = FactoryProducer::CreateFactory("ProductA");
AbstractProductA * abstract_A = abstract_factory->createProductA("ProductA1");
abstract_A->execute();

abstract_factory = FactoryProducer::CreateFactory("ProductA");
abstract_A = abstract_factory->createProductA("ProductA2");
abstract_A->execute();

abstract_factory = FactoryProducer::CreateFactory("ProductA");
abstract_A = abstract_factory->createProductA("ProductA3");
abstract_A->execute();

abstract_factory = FactoryProducer::CreateFactory("ProductB");
AbstractProductB * abstract_B = abstract_factory->createProductB("ProductB1");
abstract_B->process();

abstract_factory = FactoryProducer::CreateFactory("ProductB");
abstract_B = abstract_factory->createProductB("ProductB2");
abstract_B->process();

abstract_factory = FactoryProducer::CreateFactory("ProductB");
abstract_B = abstract_factory->createProductB("ProductB3");
abstract_B->process();

Jak można zauważyć, obiekt Fabryki Abstrakcyjnej wystarczy zdefiniować tylko jeden.

I jeszcze diagram klas, który udało mi się wygenerować przy pomocy IDE.
Niby prosta i oczywista rzecz, ale fajny ficzer:
Spoiler

Co o tym sadzicie?
Proces tworzenia produktów przez użytkownika końcowego jest całkowicie abstrakcyjny.

Nie wiem czy przypadkiem znowu nie została złamana zasada OCP, no bo jeżeli dodajemy nowy produkt konkretny do rodziny to musimy modyfikować konkretna fabrykę odpowiadająca tej rodzinie.
Dalej...
Jeżeli dodajemy nowa rodzinę to musimy ja uwzględnić w budowniczym fabryk.

Nie wyobrażam sobie jak można by to inaczej rozegrać.
No bo w sumie to taki enum tylko zrobiony na około. Jedyne co jest wymagane to znajomość nazw klas które chce się użyć.

Implementacja która jest podana w pierwszym poście jest bardzo sztywna i nie pozwala na dowolne tworzenie obiektów.

// edit
@Adanos
Tak właśnie zerkam sobie na wiki odnośnie OCP.
Pisza tam, ze:
Cytuj
elementy systemu takie, jak klasy, moduły, funkcje itd. powinny być otwarte na rozszerzenie, ale zamknięte na modyfikacje
Czyli rozszerzając klasy funkcje obsługi nowych klas lamie czy nie lamie tej zasady?
No bo modyfikacja to zmiana już istniejącej funkcjonalności. Natomiast rozszerzanie no to nowa funkcjonalność, tak?
 

Adanos

Adanos

Administrator
Szara eminencja
posty5204
Propsy3870
ProfesjaProgramista
  • Administrator
  • Szara eminencja
Nie, nie. Co jeśli będziesz potrzebował 100 fabryk? Będziesz robił 100 ifów? Zrób to tak jak wcześniej pisaliśmy z Avallachem.

Zasada otwarte-zamknięte, dobrze myślisz. Po prostu nie powinno się zmieniać kodu, który już działa. Rozszerzasz przede wszystkim interfejs klasy, dodając nową metodę do interfejsu, implementując w klasie i nie zmieniając żadnej innej metody.

inż. Avallach

inż. Avallach

Administrator
posty7661
Propsy5239
NagrodyV
ProfesjaProgramista
  • Administrator
Już lepiej zmieniać kod niż interfejs. To naprawdę bardzo ważne. Chyba że to niezbędne, bo dotyczy np rozbudowy fabryki o nowe przypadki użycia.

U mnie w pracy mamy obowiązkową (i z perspektywy czasu widzę że praktyczną) zasadę, żeby fabryki zawsze były w tym sensie abstrakcyjne (użytkownik wie jedynie o interfejsie, nie wie jakie są implementacje) i nie miały *żadnej* logiki. Nawet tej służącej do wyboru klasy do instancjonowania. Po prostu metody dotyczące różnych przypadków użycia instancjonują inne implementacje. Logika służąca do dokonania wyboru znajduje się u klienta. Fabryka tylko składa klocki do kupy według wybranego z listy przepisu. Dzięki temu nie trzeba jej testować, a testowanie wołania konstruktorów potrafi być koszmarkiem (normalnie nie powinny mieć efektów ubocznych).

Właściwie każda klasa implementuje jakiś interfejs, a każdy interfejs ma taką swoją własną fabrykę abstrakcyjną. Nawet jeśli istnieje tylko jedna prawdziwa implementacja, to jest to potrzebne choćby z tego względu że w unit testach stosujemy mocki i potrzebujemy podstawiać zmockowaną implementację. Swoją drogą ma to też masę innych zalet. Dużo łatwiej "nieinwazyjnie modyfikować" (OCP jest tutaj  bardzo ważnym sojusznikiem) moduły które korzystają ze zależności po interfejsach, a nie implementacji. Po prostu podajemy naszą, zmodyfkowaną implementację, choćby opakowującą oryginał. Zmieniamy zachowanie istniejącej już części systemu przez dodanie jednego nowego obiektu. Nawet bez ruszenia interfejsów.

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer
Adanos tak teraz przeglądam Twoja implementacje fabryki i tam chyba tez nie wszystko gra tak jak powinno.
Gdybyś chciał dodać nowy produkt konkretny do interfejsu animal np żyrafę to musisz dla niej opracować nowa fabrykę: GiraffeFactory.
Jeśli chciałbyś dodać nowa rodzinę produktów np klasę abstrakcyjna określająca pracowników zoo: ZooEmployee to znowu określając produkty konkretne, np ZooDirector to znowu musisz definiować nowe fabryki specjalnie dla produktów konkretnych.

No i jaki jest sens implementowania fabryki dla np tygrysa skoro wiadomo, ze tygrys będzie tylko jeden (no bo to produkt konkretny)?

Tak to wynika z Twojej implementacji (no chyba, ze to ja znowu czegoś nie rozumiem)
Czyli w sumie podobnie działa moja fabryka której implementacje pokazałem w pierwszym poście.

edit
Dobra chyba doszedłem do wniosku jakoby fabryka abstrakcyjna nie służyła bezpośrednio do produkcji produktów konkretnych (co tez chciałem osiągnąć druga implementacja) tylko do konstrukcji obiektów złożonych z tych produktów konkretnych.
Tzn ukrywamy implementacje rodzin, a udostępniamy implementacje fabryk konkretnych.
Tworzenie produktów konkretnych za pomocą fabryki abstrakcyjnej jest bez sensu no bo w sumie jak to już Av zauważył można zawołać bezpośrednio konstruktor i po problemie .

Czyli fabryka przedstawiona w pierwszym poście jest ok, tak?
 

inż. Avallach

inż. Avallach

Administrator
posty7661
Propsy5239
NagrodyV
ProfesjaProgramista
  • Administrator
Tworzenie produktów konkretnych za pomocą fabryki abstrakcyjnej jest bez sensu no bo w sumie jak to już Av zauważył można zawołać bezpośrednio konstruktor i po problemie .
Ciągle nie załapałeś.
Tworzysz te obiekty wołając specjalnie napisaną do tego w fabryce metodę, ale ta metoda nie mówi co to będzie za implementacja. Mówi tylko do czego jest przeznaczona. W ten sposób klient widzi tylko abstrakcję (czyli interfejs który jest implementowany przez otrzymywany obiekt). Klient dokonuje wyboru przeznaczenia, ale to fabryka wie którą implementację tworzy. Mamy podział ról. To jest mega przydatne, podawałem już żę umożliwia między innymi testowanie przez mocki i rozszerzanie systemu bez naruszania Open-Closed Principle.

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer

Wonski
Gry (themodders@telegram)

AbstractFactory - zbyt mało abstrakcji
#11 2016-07-13, 21:17(Ostatnia zmiana: 2016-07-13, 21:28)
Której fabryce bo już sam nie wiem o co chodzi?
W interfejsie dla fabryk konkretnych czy już w samych fabrykach konkretnych?
Cos mi tu nie pasuje.
Przecież fabryki konkretne, w pierwszej implementacji, to gotowe przepisy na obiekty które agregują interfejsy rodzin produktów. I już w samych fabrykach konkretyzujemy o jakie produkty nam chodzi.
Gdzie tam jest miejsce na dodatkowe metody?

Ale nawet jeśli, to jak mam dokonać konkretyzacji o jaki produkt konkretny mi chodzi?
Przez enuma czy inne consty odpada bo jak już ustaliliśmy jest to bez sensu.
A tworzyć obiekt tylko po to by się moc do niego odwołać przez interfejs jest bez sensu. No bo przecież widzimy implementacje.
A robić tak jak adanos, czyli tworzyć fabrykę konkretna dla każdego produktu to chyba tez nieelastyczne.

Może jak będziesz miał chwile to podaj jakaś szkicowa implementacje bo serio niewyobrazam sobie jak skonkretyzowac obiekt nie podajac jego implementacji lub po prostu symbolu.

Cos opornie to idzie :D
 

inż. Avallach

inż. Avallach

Administrator
posty7661
Propsy5239
NagrodyV
ProfesjaProgramista
  • Administrator

inż. Avallach
Administrator

AbstractFactory - zbyt mało abstrakcji
#12 2016-07-13, 21:23(Ostatnia zmiana: 2016-07-13, 21:36)
Fabryka abstrakcyjna to po prostu interfejs. A implementuje go fabryka konkretna. Klient widzi tylko interfejs fabryki (czyli fabrykę abstrakcyjną). O implementacjach tworzonych obiektów wie tylko implementacja fabryki (to co nazywasz fabryką konkretną).
Cytując definicję z Wikipedii:
In normal usage, the client software creates a concrete implementation of the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part of the theme.

Wyobraź sobie że mamy fabrykę abstrakcyjną IFabrykaOwoców, która produkuje instancje klasy IOwoc. Tyle wie klient.
Ale IOwoc to przecież tylko interfejs. O jego implementacjach wiedzą tylko fabryki konkretne FabrykiOwoców (które z kolei implementują IFabrykaOwoców).

Klient widzi:
IFabrykaOwoców <-- właśnie to jest abstrakcyjna fabryka
IOwoc

Implementacja widzi:
FabrykaOwocówEgzotycznych
Ananas
Pomarańcza
FabrykaOwocówLeśnych
Jagoda
Jeżyna

Przykładowe metody dla IFabrykaOwoców:
dajMiCośCiemnego() -> return new Jagoda();
dajMiCośBrudzącego() -> return new Jagoda();
dajMiCośDużego() -> return new Ananas();

Klient nie wie jakie istnieją implementacje dla IOwoc, tylko fabryka wie które są jak wykorzystane.

Wonski

Wonski

Gry (themodders@telegram)
radio engineer
posty256
Propsy91
ProfesjaProgramista
  • Gry (themodders@telegram)
  • radio engineer

Wonski
Gry (themodders@telegram)

AbstractFactory - zbyt mało abstrakcji
#13 2016-07-13, 22:37(Ostatnia zmiana: 2016-07-13, 22:56)
Chyba o to chodzi:
class IFruit
{
public:
virtual void execute() = 0;
};

class Blackberry :public IFruit
{
public:
virtual void execute() override { cout << "This is Blackberry" << endl; }
};

class Blueberry :public IFruit
{
public:
virtual void execute() override { cout << "This is Blueberry" << endl; }
};

class Pineapple :public IFruit
{
public:
virtual void execute() override { cout << "This is Pineapple" << endl; }
};

class Orange :public IFruit
{
public:
virtual void execute() override { cout << "this is Orange" << endl; }
};

//////////////////////////////////////////////////////////////////////////////

class IFruitFactory
{
public:
virtual IFruit * giveMeSomething() = 0;
};

class ForestFruitFactory :public IFruitFactory
{
public:
IFruit * giveMeSomething() override { return new Blackberry; }
};

class TropicalFruitFactory :public IFruitFactory
{
public:
IFruit *giveMeSomething() override { return new Orange; }
};

/////////////////////

class ConcreteFruit
{
public:

static IFruit * createFruit(IFruitFactory * helper)
{
return helper->giveMeSomething();
}
};

Test:
ConcreteFruit::createFruit(new TropicalFruitFactory)->execute();

O to chodzi?

//edit
wychodzi na to, ze musiałbym robić tak jak w implementacji adanosa. Dla każdego nowego produktu przygotowywać nowa fabrykę. No bo jak inaczej dostane produkt np Blueberry lub Pineapple bez ujawniania implementacji?
 

inż. Avallach

inż. Avallach

Administrator
posty7661
Propsy5239
NagrodyV
ProfesjaProgramista
  • Administrator
W tym co napisałeś, fabryką abstrakcyjną jest "helper" (czyli IFruitFactory).

Absolutnie nie musisz tworzyć nowej fabryki dla każdego produktu. Wyciągasz błędny wniosek że każda implementacja fabryki musi instancjonować jeden typ, inny niż pozostałe implementacje fabryk. Tymczasem we wzorcu fabryki abstrakcyjnej możesz mieć nawet tylko jedną implementację fabryki. Ma za to ona różne metody, które mogą (ale nie muszą) tworzyć różne implementacje.

Popatrz jeszcze raz na przykład który podałem w poprzednim poście - dobór tego co jest zwracane jest bardzo celowy. Możesz mieć różne implementacje fabryki które korzystają pod spotem z tych samych typów. Możesz mieć implementację fabryki która dla wielu metod zwraca ten sam typ. Możesz robić cokolwiek na co tylko pozwala ci interfejs - i właśnie na tym polega abstrakcyjność tej fabryki. Ograniczenia typu "każdy typ musi mieć swoją osobną fabryką" albo "każdy typ musi mieć swoją metodę w fabryce" to właśnie to co zabiera ci z tego abstrakcję, przez nałożenie sztucznych (i niemożliwych do weryfikacji ograniczeń).

Zgaduję że twój problem ze zrozumieniem o co chodzi wynika głównie z tego że próbujesz zrobić to czysto syntetycznie zamiast na prawdziwym przypadku gdzie fabryka abstrakcyjna jest rzeczywiście potrzebna i możliwa do zastosowania.


0 użytkowników i 1 Gość przegląda ten wątek.
0 użytkowników
Do góry