Tengo el siguiente código para la «fábrica» patrón de diseño de la aplicación.

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);
}

Pero he oído que se puede hacer de una mejor manera el uso de «las plantillas de C++». Alguien puede ayudar, cómo se realiza y cómo plantilla enfoque es mejor que este?

Cualquier pensamiento

6 Comentarios

  1. 4

    En el ejemplo que has publicado, ni una fábrica o en una plantilla de enfoque tiene sentido para mí.
    Mi solución implica un miembro de datos en la Pluma de clase.

    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;
    }

    Por supuesto, el color de la clase tendría que ajustarse a la API de dibujo de utilizar-y tal vez ser la forma más avanzada de este (diferentes espacios de color, etc).

    ¿Por qué no las plantillas?

    La razón no tiene sentido el uso de las plantillas, es que (presumiblemente) la única diferencia entre las diferentes operaciones de dibujo es el color de la variable. Así, mediante el uso de plantillas (o manualmente declarando diferentes clases, como lo hizo), tendrá que duplicar código similar. Esto hará que su programa grande y lento hacia abajo.

    Así, el sorteo de la función debe tomar el color como un argumento, o (como en mi ejemplo) tienen el color como una clase miembro de datos.

    • ¿Cuál es el punto de la copia-por-referencia constructor en el código aquí ?
    • Eso se llama un constructor de copia, y se llaman a veces sorprendente. Como el código está escrito, no hay gran punto a es, a parte de decir que cuando copio un lápiz, lo que hago es copiar el color de la pluma. Si Color era más compleja, esto podría tener le importaba mucho más.
  2. 12

    Otra forma es registrar dinámicamente un creador función a una dinámica de Fábrica objeto.

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

    Un efecto interesante en hacer como este es que la estática variable de tipo bool BluePen-creator-registered serán establecidos antes de la main() comienza haciendo el registro automatizado.

    Estas líneas, a veces están ordinario a través de macros, es decir, como

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

    …y se utiliza cerca de el constructor

    METAIMPL( BluePen ); //auto registers to the Factory
    BluePen::BluePen() : Pen() {
    //something
    }

    A continuación, la Fábrica de la tarea será la de la tienda y la búsqueda de estos creador funciones. Puedo dejar el resto como el ejercicio 😉 es decir, el uso de un METADECL macro

    Si quieres más info, ver aquí en el capítulo 4.1 Meta Información que también incluye un método para ampliar para incluir posibilidades para inspector características

    Aprendí esto de usar ET++ que era un proyecto a puerto viejo MacApp a C++ y X11. En el esfuerzo de Eric Gamma etc empecé a pensar en Patrones de Diseño

    Y…(7 de Mayo de 2011) Finalmente llegó a empujar un ejemplo a github
    https://github.com/epatel/cpp-factory

  3. 5

    Su fábrica está bien. Me tome el BluePen y así sucesivamente fueron solo ejemplo de nombres de la clase. Puede utilizar plantillas, si las siguientes condiciones se cumplan:

    Cuando se conoce en tiempo de compilación (que yo.e al escribir el código) que desea un tipo específico devuelto, a continuación, utilizar una plantilla. De lo contrario, usted no puede.

    Que significa en el código, que puede hacer esto:

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

    Tener eso en su lugar, usted puede utilizar que como

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

    Pero que la plantilla argumento, el BluePen, no puede ser una variable que se establece a un tipo en tiempo de ejecución. En tu ejemplo, se pasa una cadena, que, por supuesto, puede establecer en tiempo de ejecución. Así que, al leer que usted puede utilizar las Plantillas de C++, entonces, la recomendación es sólo condicionalmente verdadera, entonces, cuando su decisión, de la que el pen para crear, ya se ha hecho en tiempo de compilación. Si esa condición se ajusta, entonces la solución de plantilla es el cosa correcta de hacer. No le costará nada en tiempo de ejecución, y será exactamente lo que usted necesita.

  4. 3

    Declarando especial vacía clases de colores, puedes hacer de todo con el uso de plantillas. Esto exige que cada elección de colores para estar disponible en tiempo de compilación. Al hacer esto, se evita tener que utilizar una base de clase con métodos virtuales.

    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 >());
    }
  5. 1

    Podría escribir un objeto genérico de la fábrica de la clase como una clase de plantilla (o utilizar el bien descrito en este gamedev.net artículo).

    De esta manera, si usted tiene más de una fábrica en el código, es menos trabajo para definir cada fábrica.

  6. 0

    Como un suplemento a mi otra respuesta, sólo para discutir el patrón de Fábrica vs plantilla de uso:

    Los principales (y más simple) que la razón para el uso de plantillas, es que su código es idéntico en todos los casos, excepto para los tipos de datos que funciona en. Los ejemplos aquí son los contenedores STL.
    Sería posible escribir una fábrica función createVector(«cadena»), y el tipo de cada contenedor de forma manual, pero esto es claramente sub-óptima.

    Incluso cuando el código es diferente, no sólo tipos de datos, es posible utilizar la plantilla de especializaciones -, pero, en muchos casos, una fábrica función tendría más sentido.

    Como un ejemplo, considere la posibilidad de abstracción de base de datos de la biblioteca. Sería posible utilizar la plantilla de especializaciones, de modo que la biblioteca podría ser utilizado como «db::controlador». Pero esto obligaría a que escriba el tipo de base de datos en todas partes en el código (hacer de la biblioteca un poco inútil en el primer lugar…), o realizar un caso de un tipo de interfaz db::controlador de clase.

    En este ejemplo, es más intuitivo para decir db::get_driver(odbc) y obtener la clase adecuada de fundición para el tipo de interfaz.

Dejar respuesta

Please enter your comment!
Please enter your name here