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)
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.