2015-07-18 2 views
3

Рассмотрим следующий код:Лучший способ для реализации абстрактной модели фабрики

#include <stdio.h> 


// ============================= 


class Shape{ 
public: 
    virtual ~Shape(){}; 

    virtual void process() = 0; 
}; 

class Triangle : public Shape{ 
public: 
    virtual void process() override { 
     printf("BBB\n"); 
    } 
}; 


// ============================= 


/* option 1 */ 
class TriangleProducer{ 
public: 
    Triangle factory(){ 
     return Triangle {}; 
    } 
}; 



/* option 2 */ 
class PtrShapeProducer{ 
public: 
    Shape *factory(){ 
     return new Triangle {}; 
    } 
}; 



/* option 3 */ 
class PimplShape : public Shape{ 
    Shape *sh; 
public: 
    PimplShape(Shape *sh) : sh(sh){ 
    } 

    virtual ~PimplShape() override{ 
     delete sh; 
    } 

    virtual void process() override { 
     sh->process(); 
    } 
}; 

class PimplShapeProducer{ 
public: 

    PimplShape factory(){ 
     return new Triangle {}; 
    } 
}; 


// ============================= 


int main(){ 
    TriangleProducer f1; 
    Triangle tri = f1.factory(); 
    tri.process(); 



    PtrShapeProducer f2; 
    Shape & sh = *f2.factory(); 
    sh.process(); 
    delete & sh; 



    PtrShapeProducer f3; 
    PimplShape psh = f3.factory(); 
    psh.process(); 



    return 0; 
} 

ВАРИАНТ 1

Это хорошо, но это на самом деле не достичь полиморфизм. Тип возврата известен, и вы должны его сопоставить. Можно добавить auto вместо Triangle, но это ничего не меняет, кроме простого рефакторинга.

ВАРИАНТ 2

Это как Java и PHP делает это. Но я понял, что «необработанные» указатели нежелательны в C++. Можно добавить std::unique_ptr, но еще раз это ничего не изменит, кроме прочего delete. не

ВАРИАНТ 3

Это то, что кто-то предложить здесь некоторое время назад - работает хорошо, не «сырые» указатели, не удалять. Но это так много кода, и слишком сложный - кажется причудливым, но не правильным.

ВАРИАНТ 4 (не реализованы здесь)

Игра с константными ссылками - однако они Const, и это не меняет «заводское» типа возвращаемого значения. Я думаю, это больше похоже на вариацию, это не реальный вариант.

Любой другой вариант, который мне не хватает?
И что было бы лучше всего пойти?

+4

Определите «Лучший». Программирование всегда просто компромиссы, нет серебряной пули. –

+1

Возможно, вы запросили лучший способ реализовать *** заводской шаблон ***? «Полиморфизм», похоже, не то, что вы сравниваете здесь. –

+0

@DrewDormann - я думаю, что оба связаны. Я ищу «абстрактный шаблон фабрики» - например. как заводские, так и возвращаемые классы являются чистыми виртуальными – Nick

ответ

0

Ваши заводы проходят мимо собственности. Есть еще одна альтернатива этому аспекту; вместо огибают собственности, вы можете сделать завод собственные объекты:

class Factory { 
    public: 
     ~Factory() { for(int i=0;i<vec.size();i++) delete vec[i]; } 
     Shape &build_obj() { 
     Shape *sh = new Triangle; 
     vec.push_back(sh); 
     return *sh; 
     } 

    private: 
     void operator=(const Factory &); 
     std::vector<Shape*> vec; 
    }; 
+0

Это предполагает, что фабрика _should_ принадлежит объекту. Вы могли бы просто вернуть «std:: unique_ptr» и передать право собственности очевидным и идиоматическим способом. Если вы хотите выразить право собственности здесь, вы можете вернуть ссылку const на 'std :: unique_ptr'. –

+0

@cap obvlious; нет, вот где идиоматический C++ поступил не так, когда они настаивают на том, чтобы каждый раз обходить собственность. – tp1

+1

Вот почему я сказал _might_. Возвращение необработанного указателя является вполне приемлемым представлением указателя, не являющегося владельцем, но ваш пост на самом деле не затрагивает ничего. –

6

Я думаю, что наиболее идиоматических современный метод C++ является один вы упоминаете мимоходом, но игнорировать. Верните a std::unique_ptr<Shape>.

Это безопасно, четко выражает право собственности, поддерживает полиморфизм и не нуждается в большом количестве кода.

class ShapeFactory { 
public: 
    std::unique_ptr<Shape> create(){ 
    return std::make_unique<Triangle>(); 
    } 
}; 

Но я бы не хотел утверждать, что это был «лучший» метод.

Ваш PimplShape в варианте 3 на самом деле очень похож на unique_ptr только менее общий или проверенный.

+0

Это решение по сравнению с вариантом 3 теряет способность создавать собственные функции-члены для класса handle, поскольку unique_ptr исходит из стандартной библиотеки – tp1

+1

@ tp1 Все функции публичного члена 'Shape' доступны непосредственно через' operator-> ', если вам нужно больше функциональности, чем это, тогда обязательно создайте свой собственный класс. Хотя я не уверен, что буду называть его« handle » класс "в этот момент, это просто класс, состоящий из' Shape', и лично я все равно буду использовать 'unique_ptr' внутри. –

Смежные вопросы