Pergunta

Eu tenho o seguinte código para "fábrica" ??implementação padrão de projeto.

class Pen{
public:
     virtual void Draw() = 0;
};

class RedPen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

class BluePen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

auto_ptr<Pen> createPen(const std::string color){
     if(color == "red")
         return auto_ptr<Pen>(new RedPen);
     else if(color == "blue")
         return auto_ptr<Pen>(new BluePen);
}

Mas ouvi dizer que ele pode ser feito de uma maneira melhor usando "++ templates C". Alguém pode ajudar como é feito e como abordagem de modelo é melhor do que isso?

Todos os pensamentos

Foi útil?

Solução

No exemplo que você postou, nem uma fábrica ou uma abordagem de modelo faz sentido para mim. Minha solução envolve um membro de dados na classe Pen.

class Pen {
public:
    Pen() : m_color(0,0,0,0) /* the default colour is black */
    {            
    }

    Pen(const Color& c) : m_color(c)
    {
    }

    Pen(const Pen& other) : m_color(other.color())
    {
    }

    virtual void Draw()
    {
        cout << "Drawing with a pen of color " << m_color.hex();
    }
    void setColor(const Color& c) { m_color = c; }
    const Color& color() const { return m_color; }
private:
    Color m_color;
};

class Color {
public:
    Color(int r, int g, int b, int a = 0) :
        m_red(r), m_green(g), m_blue(other.blue()), m_alpha(a)  
    {
    }

    Color(const Color& other) : 
        m_red(other.red()), m_green(other.green()), 
        m_blue(other.blue()), m_alpha(other.alpha())
    {
    }

    int red() const { return m_red; }
    int green() const  { return m_green; }
    int blue() const { return m_blue; }
    int alpha() const { return m_alpha; }

    std::string hex() const
    {
        std::ostringstream os;
        char buf[3];
        os << "#";

        sprintf(buf, "%2X", red());
        os << buf;

        sprintf(buf, "%2X", green());
        os << buf;

        sprintf(buf, "%2X", blue());
        os << buf;

        sprintf(buf, "%2X", alpha());
        os << buf;

        return os.str();
    }

private:
    int m_red;
    int m_green;
    int m_blue;
    int m_alpha;
}

É claro, a classe cor teria que ser ajustado para a API de desenho que você usa -. E, talvez, ser muito mais avançado do que este (espaços de cores diferentes, etc)

Por que não templates?

A razão não faz sentido usar modelos, é que (presumivelmente) a única diferença entre as diferentes operações de desenho é a variável cor. Então, usando modelos (ou manualmente declarar classes diferentes, como você fez), você vai duplicar o código similar. Isso fará com que seu programa grande, e retardá-lo.

Assim, a função de sorteio deve assumir a cor como um argumento, ou (como no meu exemplo) tem a cor como um membro de dados de classe.

Outras dicas

Outra forma é registrar dinamicamente um criador função para um objeto Fábrica dinâmica.

BluePen *create_BluePen() { return new BluePen; }
static bool BluePen_creator_registered = 
                       Factory::instance()->registerCreator("BluePen", 
                                                            create_BluePen);

Um efeito interessante em fazer como este é que a variável bool estático BluePen-creator-registered será definido main() antes começa a fazer assim a registrar automatizado.

Estas linhas são por vezes feitas através de macros comuns, ou seja, como

#define METAIMPL( _name ) \
_name *create_ ## _name() { return new _name; } \
static bool _name ## _creator_registered = \
                        Factory::instance()->registerCreator(# _name, \
                                                             create_ ## _name)

... e usados ??perto do construtor

METAIMPL( BluePen ); // auto registers to the Factory

BluePen::BluePen() : Pen() {
   // something
}

Em seguida, a tarefa do Fábrica será a loja e pesquisar estas criador funções. Deixo o resto como o exercício ;), ou seja o uso de uma macro METADECL

Se você quiser mais informações, consulte aqui ao abrigo do capítulo 4.1 Meta Informação , que também inclui um método para expandir para incluir possibilidades de inspetor apresenta

Eu aprendi isso de usar ET ++ que era um projeto para porto velho MacApp para C ++ e X11. No esforço de que Eric Gamma etc começou a pensar em Design Patterns

E ... (7 de Maio de 2011) Finalmente veio ao redor para empurrar um exemplo para github
https : //github.com/epatel/cpp-factory

Sua fábrica está bem. Eu levá-la a BluePen e assim por diante foram apenas exemplos de nomes de classe. Você pode usar modelos, se a seguinte condição for atendida:

Quando você sabe em tempo de compilação (ou seja, quando você escreve código) que você quer um tipo específico voltou, em seguida, usar um modelo. Caso contrário, você não pode.

Isso significa que no código, que você pode fazer isso:

template<typename PenType>
auto_ptr<Pen> createPen(){
    return auto_ptr<Pen>(new PenType);
}

Tendo isso em lugar, então você pode usar isso como

...
auto_ptr<Pen> p = createPen<BluePen>();
...

Mas esse modelo-argumento, o BluePen, não pode ser uma variável que está definido para um tipo em tempo de execução. No seu exemplo, você passar uma string, que pode, naturalmente, ser definida em tempo de execução. Então, quando você ler que você pode usar C ++ Templates, em seguida, que a recomendação é apenas condicionalmente verdade - então, quando a sua decisão, de que a pena de criar, já é feito em tempo de compilação. Se que se encaixa condição, então a solução de modelo é o coisa certa a fazer. Não vai custar nada em tempo de execução, e será exatamente o que você precisa.

Ao declarar aulas vazias especiais para cores, você pode fazer tudo usando modelos. Isso exige que cada escolha da cor a ser disponibilizado em tempo de compilação. Ao fazer isso, você evita ter que usar uma classe base com métodos virtuais.

struct Red{};
struct Blue{};

template < typename Color >
class Pen{};

template <>
class Pen< Red >
{
     void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

template <>
class Pen< Blue >
{
     void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

template < typename Color >
std::auto_ptr< Pen< Color > > createPen()
{
     return auto_ptr< Pen< Color > >(new Pen< Color >());
}

Você poderia escrever uma classe genérica objeto de fábrica como uma classe de modelo (ou use esse bem descrito nesta gamedev.net artigo ).

Desta forma, se você tiver mais de uma fábrica em seu código, é menos trabalho para definir cada fábrica.

Como um complemento para a minha outra resposta, apenas para discutir a fábrica modelo padrão vs. Uso:

A principal (e mais simples) razão para usar modelos, é que seu código é idêntico em todos os casos, exceto para os tipos de dados que funciona no. Exemplos aqui são os recipientes STL. Seria possível escrever um createVector função de fábrica ( "string"), e digitar cada recipiente manualmente -. Mas este é claramente sub-óptima

Mesmo quando difere de código, não apenas tipos de dados, é possível usar especializações modelo -. Mas em muitos casos, uma função fábrica faria mais sentido

Como exemplo, considere uma biblioteca de abstração de banco de dados. Seria possível usar especializações modelo, de modo que a biblioteca poderia ser usado como "db :: motorista". Mas isso iria forçá-lo a digitar o tipo de banco de dados em todos os lugares no código (fazendo a biblioteca meio inútil em primeiro lugar ...), ou executar um caso a uma classe db :: motorista tipo de interface.

Neste exemplo, é mais intuitivo para dizer db :: get_driver (ODBC) e receber de volta o elenco classe adequada para o tipo de interface.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top