Как элегантно работать с абстрактной фабрикой и массивами как типом возврата в C ++? - PullRequest
3 голосов
/ 23 декабря 2011

Имея в виду абстрактный шаблон фабрики, представьте, что у вас есть иерархия классов , в которой конкретные фабрики переопределяют виртуальный метод createButton * s *, для которого требуется вернуть более широкий набор кнопок,Что может быть элегантным обходным путем для решения этой проблемы, поскольку C ++ поддерживает только ковариантные типы возвращаемых данных?dynamic_cast?

Модифицированная версия примера GoF в соответствии с моими требованиями:

class Button {
public:
        virtual void paint() = 0;
        virtual ~Button(){
        }
};

class WinButton: public Button {
public:
        void paint() {
                cout << "I'm a WinButton";
        }
};

class OSXButton: public Button {
public:
        void paint() {
                cout << "I'm an OSXButton";
        }
};

class GUIFactory {
public:
        virtual Button * createButtons() = 0;
        virtual ~GUIFactory(){
        }
};

class WinFactory: public GUIFactory {
public:
        WinButton* createButtons() {
                return new WinButton[2];
        }

        ~WinFactory(){
        }
};

class OSXFactory: public GUIFactory {
public:
        OSXButton* createButtons() {
                return new OSXButton[2];
        }

        ~OSXFactory(){
        }
};

И каркас приложения:

Application(GUIFactory * factory) {
                Button* buttons = factory->createButtons();
                for(...) {...}
        }

Ответы [ 3 ]

2 голосов
/ 23 декабря 2011

Это невозможно сделать.Спросите себя, будет ли звонящий delete или delete[] это?Как они узнают, сколько элементов в массивах?Нет способа решить любую из этих проблем с помощью необработанного возврата указателя.Таким образом, простой ответ на ваш вопрос заключается в том, что этого нельзя сделать, и вы даже не хотите этого делать.Вызываемый должен знать тип возврата - будь то одна кнопка или много, и как их убрать, когда он закончил.

Вы даже не можете получить доступ не первый элемент без вызова Undefined Behavior.

Фабрика должна создавать отдельные объекты.Если вам нужно более одной, вызывайте фабричную функцию более одного раза.

Даже если у вас был магический код, который мог обрабатывать более одной кнопки одновременно, когда они ожидали только одну (wtf?), Вам нужнобезопасно вернуть более одного.Это значит std::vector<smart_pointer<Button>>.

1 голос
/ 23 декабря 2011

Вы не хотите создать массив в фабричном методе и вернуть указатель клиенту. Таким образом, вы не представляете, сколько кнопок, так что вы действительно ничего не можете сделать с тем, что получаете.

В C ++ 11 вы можете вернуть std::vector<shared_ptr<Button>> или даже std::vector<unique_ptr<Button>>.

Вероятно, я бы выбрал это решение: вернуть какую-то пользовательскую коллекцию Button "только для чтения", которая предоставляет счетчик и индексатор и ничего более. Возможно, добавьте итераторы, если это будет соответствовать вашим сценариям.

class ButtonCollection
{
public:
    size_t getCount() const {}
    Button& getAt(size_t index) {}
    const Button& getAt(size_t index) const {}

private:
    friend class GUIFactory;

    ButtonCollection() {}
    add(Button* button) {}
    // Or in C++11:
    // add(std::unique_ptr<Button> button) {}
};

С вашим скелетом приложения:

Application(GUIFactory& factory)
{
   std::auto_ptr<ButtonCollection> buttons = factory.createButtons();
   // Or in C++11:
   //std::unique_ptr<ButtonCollection> buttons = factory.createButtons();
   for(...) {...}
}
0 голосов
/ 23 декабря 2011

Рассмотрите возможность использования boost::ptr_vector или boost::ptr_array
http://www.boost.org/doc/libs/1_48_0/libs/ptr_container/doc/ptr_vector.html

...