3 Stimmen

Implementierung von Fabrikmethoden - C++

Ich habe den folgenden Code für die Implementierung des Entwurfsmusters "Fabrik".

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

Aber ich habe gehört, dass man das besser mit "C++-Vorlagen" machen kann. Kann jemand helfen, wie es getan wird und wie Vorlage Ansatz ist besser als diese?

Irgendwelche Gedanken

13voto

epatel Punkte 45365

Eine andere Möglichkeit besteht darin, dynamisch eine Ersteller Funktion auf ein dynamisches Factory-Objekt.

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

Ein interessanter Effekt dieser Vorgehensweise ist, dass die statische bool-Variable BluePen-creator-registered wird vorher festgelegt main() beginnt, wodurch die Registrierung automatisiert wird.

Diese Zeilen werden manchmal durch gewöhnliche Makros erstellt, z.B. als

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

...und in der Nähe des Konstruktors verwendet

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

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

Die Aufgabe der Fabrik besteht dann darin, diese Daten zu speichern und nachzuschlagen. Ersteller Funktionen. Den Rest lasse ich als die Übung ;) d.h. die Verwendung eines METADECL-Makros

Wenn Sie mehr Informationen wünschen, siehe aquí unter Kapitel 4.1 Meta-Informationen die auch eine Methode zur Erweiterung um Möglichkeiten für Inspektor Merkmale

Das habe ich gelernt, als ich ET++ das war ein Projekt zur Portierung der alten MacApp nach C++ und X11. Im Zuge dessen begann Eric Gamma usw., sich Gedanken über Entwurfsmuster

Und...(7. Mai 2011) Endlich bin ich dazu gekommen, ein Beispiel auf Github zu veröffentlichen
https://github.com/epatel/cpp-factory

5voto

gnud Punkte 75549

In dem von Ihnen genannten Beispiel erscheint mir weder ein Fabrik- noch ein Vorlagenansatz sinnvoll. Meine Lösung beinhaltet ein Datenelement in der Klasse 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;
}

Natürlich müsste die Farbklasse an die von Ihnen verwendete Zeichen-API angepasst werden - und vielleicht viel fortschrittlicher sein als diese hier (verschiedene Farbräume usw.).

Warum keine Vorlagen?

Die Verwendung von Vorlagen ist deshalb nicht sinnvoll, weil der einzige Unterschied zwischen den verschiedenen Zeichenoperationen (vermutlich) die Farbvariable ist. Durch die Verwendung von Vorlagen (oder die manuelle Deklaration verschiedener Klassen, wie Sie es getan haben) duplizieren Sie also ähnlichen Code. Dadurch wird Ihr Programm groß und langsamer.

Die Zeichenfunktion sollte also entweder die Farbe als Argument annehmen oder (wie in meinem Beispiel) die Farbe als Klassendatenelement haben.

0 Stimmen

Welchen Sinn hat der Copy-by-Reference-Konstruktor in Ihrem Code hier?

0 Stimmen

Dies wird als Kopierkonstruktor bezeichnet und wird zu überraschenden Zeitpunkten aufgerufen. So wie der Code geschrieben ist, hat er keinen großen Sinn, außer zu sagen, dass ich beim Kopieren eines Stifts die Farbe des Stifts kopiere. Wenn Color komplexer wäre, hätte dies viel mehr Bedeutung haben können.

5voto

Ihre Fabrik ist in Ordnung. Ich nehme an, die BluePen und so weiter waren nur Beispielklassennamen. Sie können Vorlagen verwenden, wenn die folgende Bedingung erfüllt ist:

Wenn Sie zur Kompilierzeit (d. h. beim Schreiben des Codes) wissen, dass Sie einen bestimmten Typ zurückgeben wollen, dann verwenden Sie eine Vorlage. Andernfalls können Sie das nicht.

Das bedeutet im Code, dass Sie dies tun können:

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

Wenn Sie das haben, können Sie es wie folgt verwenden

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

Aber dieses Musterargument, die BluePen kann nicht eine Variable sein, die zur Laufzeit auf einen Typ gesetzt wird. In Ihrem Beispiel übergeben Sie einen String, der natürlich zur Laufzeit gesetzt werden kann. Wenn Sie also lesen, dass Sie C++-Vorlagen verwenden können, dann trifft diese Empfehlung nur bedingt zu - dann nämlich, wenn die Entscheidung, welcher Stift erstellt werden soll, bereits zur Kompilierzeit getroffen wird. Wenn diese Bedingung zutrifft, dann ist die Template-Lösung die das Richtige zu tun. Es wird Sie zur Laufzeit nichts kosten und genau das sein, was Sie brauchen.

3voto

Benoît Punkte 16390

Indem Sie spezielle leere Klassen für Farben deklarieren, können Sie alles mit Vorlagen machen. Dies erfordert, dass jede Farbauswahl zur Kompilierzeit verfügbar gemacht wird. Auf diese Weise vermeiden Sie, dass Sie eine Basisklasse mit virtuellen Methoden verwenden müssen.

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

1voto

RyanCu Punkte 401

Sie könnten eine generische Objektfabrikklasse als Vorlagenklasse schreiben (oder die in diesem Artikel beschriebene Klasse verwenden). gamedev.net-Artikel ).

Auf diese Weise müssen Sie, wenn Sie mehr als eine Fabrik in Ihrem Code haben, weniger Arbeit in die Definition jeder Fabrik stecken.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X