Question

J'ai le code suivant pour "usine". mise en œuvre du modèle de conception.

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

Mais j'ai entendu dire que cela pouvait être mieux fait en utilisant "Modèles C ++". Quelqu'un peut-il aider à savoir comment cela est fait et à quel point l'approche par modèle est meilleure que cela?

Toutes pensées

Était-ce utile?

La solution

Dans l’exemple que vous avez publié, ni une approche d’usine ni un modèle ne me semble logique. Ma solution implique un membre de données de la 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;
}

Bien sûr, la classe de couleur devra être ajustée à l'API de dessin que vous utilisez - et peut-être bien plus avancée que celle-ci (espaces de couleur différents, etc.).

Pourquoi pas les modèles?

La raison pour laquelle il n’a pas de sens d’utiliser des modèles est que (vraisemblablement) la seule différence entre les différentes opérations de dessin est la variable de couleur. Ainsi, en utilisant des modèles (ou en déclarant manuellement différentes classes, comme vous l'avez fait), vous dupliquez un code similaire. Cela rendra votre programme volumineux et le ralentira.

Ainsi, la fonction de dessin doit soit prendre la couleur en argument, soit (comme dans mon exemple) la couleur en tant que membre de données de classe.

Autres conseils

Vous pouvez également enregistrer de manière dynamique une fonction créateur dans un objet Factory dynamique.

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

Un effet intéressant en faisant cela est que la variable statique bool BluePen-creator-registered sera définie avant que main () ne commence ainsi à automatiser l'enregistrement.

/ p>

Ces lignes sont parfois créées à l'aide de macros ordinaires, c'est-à-dire

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

... et utilisé à proximité du constructeur

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

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

Ensuite, la tâche de l’usine sera de stocker et de rechercher ces fonctions créateur . Je laisse le reste comme l'exercice ;) c'est-à-dire l'utilisation d'une macro METADECL

Si vous souhaitez plus d'informations, voir ici . dans le chapitre 4.1 Méta-informations qui inclut également une méthode permettant d’étendre les possibilités pour les fonctionnalités de l’inspecteur

J'ai appris cela en utilisant ET ++ , qui était un projet de portage de MacApp ancien en C ++. et X11. Eric Gamma, etc., a commencé à réfléchir à Design Patterns

Et ... (7 mai 2011) Nous sommes enfin arrivés à donner un exemple à github
https : //github.com/epatel/cpp-factory

Votre usine va bien. Je suppose que BluePen et ainsi de suite ne sont que des exemples de noms de classe. Vous pouvez utiliser des modèles si la condition suivante est remplie:

  

Lorsque vous savez au moment de la compilation (c'est-à-dire lorsque vous écrivez du code) qu'un type spécifique doit être renvoyé, utilisez un modèle. Sinon, vous ne pouvez pas.

Cela signifie dans le code que vous pouvez faire ceci:

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

Après l'avoir mis en place, vous pouvez ensuite l'utiliser comme

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

Mais cet argument de modèle, le BluePen , ne peut pas être une variable définie sur un type au moment de l'exécution. Dans votre exemple, vous transmettez une chaîne, qui peut bien sûr être définie à l'exécution. Ainsi, lorsque vous lisez que vous pouvez utiliser des modèles C ++, cette recommandation n'est alors que conditionnellement vraie. Dans ce cas, votre décision, celle du stylet à créer, est déjà prise au moment de la compilation. Si cette condition convient, alors la solution de modèle est la chose à faire. Cela ne vous coûtera rien au moment de l'exécution et correspondra exactement à vos besoins.

En déclarant des classes vides spéciales pour les couleurs, vous pouvez tout faire à l'aide de modèles. Cela nécessite que chaque choix de couleur soit disponible au moment de la compilation. Vous évitez ainsi de devoir utiliser une classe de base avec des méthodes virtuelles.

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

Vous pouvez écrire une classe de fabrique d'objets génériques en tant que classe de modèle (ou utiliser celle bien décrite dans ce article de gamedev.net ).

De cette façon, si vous avez plusieurs fabriques dans votre code, la définition de chaque fabrique sera moins dure.

En guise de complément à mon autre réponse, il suffit de discuter du modèle Usine ou du modèle:

La raison principale (et la plus simple) d'utiliser des modèles est que votre code est identique dans tous les cas, à l'exception des types de données sur lesquels il fonctionne. Voici des exemples de conteneurs STL. Il serait possible d'écrire une fonction d'usine createVector ("chaîne") et de saisir chaque conteneur manuellement - mais ceci est clairement sous-optimal.

Même lorsque le code diffère, non seulement les types de données, il est possible d'utiliser des spécialisations de modèles - mais dans de nombreux cas, une fonction d'usine aurait plus de sens.

A titre d'exemple, considérons une bibliothèque d'abstraction de base de données. Il serait possible d'utiliser des spécialisations de modèles, de sorte que la bibliothèque puisse être utilisée comme "db :: driver". Mais cela vous obligerait à taper le type de base de données partout dans le code (en rendant la bibliothèque un peu inutile en premier lieu ...), ou à exécuter un cas sur une interface de type interface db :: driver.

Dans cet exemple, il est plus intuitif de dire db :: get_driver (odbc) et de récupérer la conversion de classe appropriée dans le type d'interface.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top