Frage

Ich habe den folgenden Code für „Fabrik“ Entwurfsmuster Umsetzung.

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 es in einer besseren Art und Weise getan werden kann, mit „C ++ templates“. Kann mir jemand helfen, wie es gemacht wird und wie Template-Ansatz ist besser als das?

Alle Gedanken

War es hilfreich?

Lösung

Im Beispiel Sie auf dem Laufenden, weder eine Fabrik oder ein Template-Ansatz macht Sinn für mich. Meine Lösung beinhaltet ein Datenelement in der Pen-Klasse.

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 würde die Farbklasse muss den Zeichnungs-API angepasst werden Sie verwenden -. Und vielleicht seinen Weg weiter fortgeschritten als diese (verschiedene Farbräume, etc.)

Warum nicht Vorlagen?

Der Grund ist es nicht sinnvoll, Vorlagen zu verwenden macht, ist, dass (vermutlich) der einzige Unterschied zwischen den verschiedenen Zeichenoperationen ist die Farbe variabel. Also, durch Vorlagen (oder manuell verschiedene Klassen deklariert, wie Sie getan haben), werden Sie einen ähnlichen Code duplizieren. Dadurch wird Ihr Programm groß machen, und es abbremsen.

So sollte die Zeichenfunktion entweder die Farbe als Argument, oder (wie in meinem Beispiel) die Farbe als Klassendatenelement.

Andere Tipps

Eine andere Möglichkeit ist, um dynamisch zu registrieren, ein Schöpfer Funktion eines dynamischen Factory-Objektes.

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

Ein interessanter Effekt in wie dies zu tun ist, dass die statische Bool Variable BluePen-creator-registered wird vor main() eingestellt werden beginnt damit die Herstellung von automatisierten registrieren.

Diese Linien werden manchmal durch gewöhnliche Makros, also als

gemacht
#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 der Konstruktor verwendet

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

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

Dann wird die Fabrik der Aufgabe zu speichern und Nachschlag dieser Schöpfer Funktionen. Ich lasse den Rest als die Übung ;), dh die Verwendung eines METADECL Makro

Wenn Sie weitere Informationen wünschen, finden Sie unter hier unter Kapitel 4.1 Meta Information , die ebenfalls ein Verfahren zur Erweiterung umfasst umfassen Möglichkeiten für Inspektor Eigenschaften

Das erfuhr ich von der Nutzung ET ++ , die ein Projekt zum Hafen alt MacApp zu C ++ war und X11. In dem Bestreben, es Eric Gamma usw. begonnen, darüber nachzudenken, Design Patterns

Und ... (7. Mai 2011) Schließlich kam um ein Beispiel zu GitHub
https zu schieben : //github.com/epatel/cpp-factory

Ihr Werk ist in Ordnung. Ich nehme an, die BluePen und so weiter wurde nur Beispiel Klassennamen. Sie können Vorlagen verwenden, wenn die folgende Bedingung erfüllt ist:

  

Wenn Sie bei der Kompilierung kennen (das heißt, wenn Sie Code schreiben), dass Sie eine bestimmte Art zurückgegeben werden sollen, dann eine Vorlage verwenden. Ansonsten kann man nicht.

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

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

Nachdem das fertig ist, können Sie dann verwenden, wie

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

Aber das Template-Argument, das BluePen kann keine Variable sein, die auf einen Typen zur Laufzeit festgelegt ist. In Ihrem Beispiel, übergeben Sie einen String, der zur Laufzeit festgelegt werden kann natürlich. Also, wenn Sie lesen, dass Sie C ++ Templates verwenden können, dann ist diese Empfehlung nur bedingt wahr - dann, wenn Sie Ihre Entscheidung, deren Stift zu schaffen, ist bereits während der Kompilierung durchgeführt. Wenn diese Bedingung paßt, dann die Vorlage Lösung die Richtige zu tun. Es wird Sie nicht zur Laufzeit kosten nichts, und genau das, was Sie brauchen.

Durch spezielle leere Klassen für Farben deklarieren, können Sie alles, was mit Hilfe von Vorlagen tun. Dies erfordert jede Farbe Wahl zur Compile-Zeit zur Verfügung gestellt werden. Auf diese Weise vermeiden Sie eine Basisklasse mit virtuellen Methoden verwenden.

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

Sie können eine generische Objekt Factory-Klasse als Vorlage Klasse schreiben (oder die schön in diesem gamedev.net Artikel ).

Auf diese Weise, wenn Sie mehr als eine Fabrik in Ihrem Code haben, ist es weniger Arbeit jedes Werk zu definieren.

Als Ergänzung zu meiner anderen Antwort, nur die Fabrik Muster gegen Vorlage useage zu diskutieren:

Die wichtigsten (und einfachste) Grund-Vorlagen zu verwenden, ist, dass der Code in jedem Fall identisch ist, mit Ausnahme der Datentypen es funktioniert auf. Beispiele hierfür sind die STL-Container. Es wäre möglich, einen Fabrik-Funktion createVector ( „string“) zu schreiben, und manuell jeden Container tippen -. Aber das ist eindeutig suboptimal

Auch wenn Code unterscheidet, nicht nur die Datentypen, ist es möglich Template Spezialisierungen verwenden -. Aber in vielen Fällen würde eine Fabrik Funktion mehr Sinn machen,

Als Beispiel eine Datenbank-Abstraktionsbibliothek betrachten. Es wäre möglich, Vorlage Spezialisierungen zu verwenden, so dass die Bibliothek könnte wie „db :: driver“ verwendet werden. Aber das würde Sie zwingen, den Datenbanktyp überall im Code tippen (so dass die Bibliothek irgendwie nutzlos in erster Linie ...), oder führen Sie einen Fall zu einer Schnittstellentyp db :: Treiberklasse.

In diesem Beispiel ist es intuitiver db :: get_driver (ODBC) zu sagen und die richtige Klasse Guss auf die Interface-Typ zurück.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top