Abstrakte Fabrik



  • Hallo Leute, bin schon länger mitleser hier, jedoch jetzt bei einer Hausübung für einen Unikurs auf einen eigenen Thread angewiesen 😞 ...

    Es geht dabei um die Anwendung eines abstrakten Fabrik Musters:
    Es soll ein Software Framework erstellt werden das dem Benutzer ermöglicht Formeln zur Berechnung von Umfang und Kreisfläche von Kreis, Dreieck und Rechteck über die Konsole bzw. in eine Datei auszugeben
    Für den Klienten soll die Erzeugung unabhängig von Medium und Form mithilfe einer Methode möglich sein.

    Erweiterungen der Funktionalität sollten möglichst ohne Änderungen der Codebasis möglich sein...eh klar 🕶

    Ich habe nun schon Probleme mit der Zuordnung zu den Teilnehmern:

    AbstrakteFabrik
    -> bekommt vom klienten den aufruf
    KonkreteFabrik
    -> Ich habe absolut keine Ahnung 😞
    AbstraktesProdukt
    ->Überordnung der Einteilung, in meinem Problemfall Kreis, Rechteck,...
    KonkretesProdukt
    -> was ich konkret will, in meinem Problemfall die Formeln
    Klient
    -> Der Benutzer der in der Konsole anfragt

    Geschweige den mit der Implementierung...und ich finde irgendwie auch keine Zielführenden Tutorials und Beispiele, die sind alle weit weck von meiner Aufgabenstellung...

    sg
    reinmanu



  • schau doch mal hier,(hoff ist ok wenn ich einen link poste...) dort wird alles erklärt:
    https://de.wikipedia.org/wiki/Fabrikmethode#Ein_Beispiel_in_C

    dort findest du auch ein beispiel mit "essen"... (in c++)



  • HELPERs schrieb:

    schau doch mal hier,(hoff ist ok wenn ich einen link poste...) dort wird alles erklärt:
    https://de.wikipedia.org/wiki/Fabrikmethode#Ein_Beispiel_in_C

    dort findest du auch ein beispiel mit "essen"... (in c++)

    Hallo,
    Danke, hab ich aber schon mehrfach durchgelesen, für das Beispiel verstehe ich das ganze eh...nur hab ich absolut keinen Plan wie ich das für meine Problemstellung anwenden kann, ich erzeuge ja nicht per se.
    Außerdem ist das die Fabrik, und bei mir gehts um die abstrakte Fabrik 😉

    sg reinmanu



  • verdruckt...peinlich 🙄



  • Poste doch mal die konkrete Aufgabenstellung - die wird doch sicher etwas spezifischer sein?
    Mit dem was du da zusammen geschrieben hast kann ich auch nicht wirklich mehr anfangen, als mit irgendwas aus den Fingern zu säugen.



  • Konk schrieb:

    Poste doch mal die konkrete Aufgabenstellung - die wird doch sicher etwas spezifischer sein?
    Mit dem was du da zusammen geschrieben hast kann ich auch nicht wirklich mehr anfangen, als mit irgendwas aus den Fingern zu säugen.

    Nein leider nicht, das ist die komplette Aufgabenstellung. Es gibt dann noch einen Punkt B, wo es um eine Erweiterung geht welche auch genauer beschrieben ist.

    a) Verfassen Sie ein einfaches Software-Framework, welches dem Benutzer ermöglicht, die Formeln
    zur Berechnung von Umfang und Flächeninhalt von Kreis, Dreieck und Rechteck (in einfacher Form)
    über die Konsole bzw. in eine Datei auszugeben (den Namen der Datei können Sie als Konstante direkt
    im Quellcode definieren). Für den Klienten sollte die Erzeugung der Ausgabe unabhängig vom
    Ausgabemedium (Konsole, Datei) und der Form (Kreis, Dreieck und Rechteck) jeweils mit Hilfe einer
    Methode (zB WriteInfo()) möglich sein. Die Implementierung sollte so erfolgen, dass eine evtl.
    Erweiterung der Funktionalität des Frameworks (zB zur Ausgabe der Info mittels Drucker (zu einem
    späteren Zeitpunkt und nicht von Ihnen zu erledigen)) möglichst ohne Änderung der bestehenden
    Codebasis möglich ist.
    b) Erweitern Sie die Funktionalität des Frameworks, sodass die Möglichkeit besteht Umfang und
    Flächeninhalt von Kreis, Dreieck und Rechteck anhand konkreter Werte zu berechnen. Die zur
    Berechnung notwendigen Parameter (zB Länge, Breite, Radius,...) sollen in einer eigenen Klasse Daten
    gekapselt sein. Die Zuweisung von Parameterwerten soll mittels Mutator(Set)-Methoden in der
    Daten-Klasse erfolgen erfolgen. Die Durchführung und Ausgabe der Berechnung inkl. Ergebnis (je
    nach Szenario wiederum entweder in eine Datei oder Konsole) soll mittels einer Methode mit der
    Signatur void ComputeAndWrite(Daten data) (in einer dafür geeigneten Klasse) erfolgen.
    Falls das übergebene Datenobjekt die erforderlichen Daten nicht enthält (=diese nicht mittels MutatorMethoden
    vom Benutzer gesetzt wurden), soll eine entsprechende Meldung ausgegeben werden. 
    
    WICHTIG:[list]
     [*]Es geht bei dieser Übung um die Anwendung des Abstrakte Fabrik-Musters und nicht (nur) um
    die Implementierung der geforderten Funktionalität. Eine erfolgreiche Absolvierung der Übung
    ist deshalb in erster Linie von der korrekten Verwendung des Abstrakte Fabrik-Muster abhängig.
    [*] Überlegen Sie sich BEVOR sie beginnen Code zu schreiben eine geeignete Klassenstruktur, sowie
    Schnittstellen und Signaturen für die Erstellung des Framworks. Erstellen Sie dafür ein
    Klassendiagramm (zB mittels Umbrello) und geben Sie dieses ebenfalls ab!
    [*] Verfassen sie für die Software einen geeigneten Test, welcher es erlaubt, die korrekte
    Funktionsweise der implementierten Funktionalität zu testen.
    [*] Vergessen Sie nicht Ihren Source Code ausreichend zu kommentieren![/list]
    

    sg
    reinmanu



  • Ich bin jetzt doch einen bedeutenden Schritt weiter...bezweifle doch stark das des so stimmt, weil ich jetzt anstehe...

    ich habe 3 Abstrakte Produkte, Kreis, Dreieck, Rechteck:

    #pragma once
    #include <string>
    
    class Circle
    {
    public:
    	virtual std::string formel() = 0;
    
    };
    

    , die anderen 2 analog dazu

    dann habe ich 2 konkrete Produkte: in Datei schreiben, und in Console schreiben:

    #pragma once
    #include "Circle.h"
    
    class wConsole : public Circle
    {
    public:
    	std::string Name()
        {
            return "u = 2*r*PI; A=r*r*PI";
        }
    };
    
    class wFile : public Circle
    {
    public:
    	std::string Name()
        {
            return "in Datei gepeichert";
        }
    
        fstream f;
        f.open("formeln.txt", ios::out);
        f << "u = 2*r*PI; A=r*r*PI" << endl;
        f.close();
    };
    

    , bin aber schun draufgekommen...des ist Schwachsinn, muss des als Methode machen. Kann ich das aber einfach analog dazu in eine Methode umschreiben?

    Dann habe ich eine abstrakte Fabrik, einmal die cpp Implementierung:

    aFactory* aFactory::CreateFactory(SHAPE_FACTORIES factory)
    {
        if(factory == SHAPE_FACTORIES::CIRCLE)
        {
            return new CircleFactory();
        }
    
        if(factory == SHAPE_FACTORIES::RECTANGLE)
        {
            return new RectangleFactory();
        }
    
        if(factory == SHAPE_FACTORIES::TRIANGLE)
        {
            return new TriangleFactory();
        }
    
    }
    

    und die Header Datei:

    class aFactory{
     public: 
         enum SHAPE_FACTORIES
        {
            CIRCLE,
            RECTANGLE,
            TRIANGLE
        };
    
        virtual Circle* GetCircle() = 0;
        virtual Triangle* GetTriangle() = 0;
        virtual Rectangle* GetRectangle() = 0;
    
        static aFactory* CreateFactory(SHAPE_FACTORIES factory);
    }
    

    und jetzt stehe ich mit 3 Brocken einer konkreten Fabrik da, und komme drauf das mir der Inhalt ausgeht...

    class CircleFactory : public aFactory
    {
        public:
            Circle* GetCircle()
            {
                return ??????
            }
    };
    

    Und i befürcht grad das des alles ein Schwachsinn isch was i da gmacht habe...

    sg
    reinmanu



  • Also erstmal ist deine Circle-Klasse usw. total wirr und falsch. Kannst Du bitte erstmal deine Geometrie-Klassen (Circle usw.) richtig designen? Du solltest erstmal allgemein OOD lernen, bevor du dich zu den großen Patterns stürzt. Dann kann man dir auch helfen.

    Egal, sagen wir mal deine Geo-Klassen sind korrekt designed... also eine abstrakte Geometrie-Basis, und davon Circle, Rectangle u. Triangle vererbt.

    Du solltest dir bewusst machen, welches Problem die abstrakte Fabrik lösen soll. Also auf OO-Ebene meine ich!

    Es gibt ja die Polymorphie, die es erlaubt die späte Bindung auf Klassen-Funktionen anzuwenden. Also in deinem Beispiel bei den Geo-Objekten auf die Formel-Funktionen: diese Klassenfunktion hast du nämlich _abstrakt_ gemacht.

    D.h. die Sprache C++ bietet dir die Möglichkeit Klassenfunktionen abstrakt zu machen, damit diese zur Laufzeit gebunden werden.

    Geht das denn aber auch mit Konstruktoren? Nein, geht es nicht. Hier hat die Sprache ein Defizit! Man kann Konstruktoren nicht zur Laufzeit binden. Also muss ein Workaround her. Und genau das macht die abstrakte Fabrik.

    Du hast mehrere Klassen (alles nachfolgende ist Pseudocode):

    class GeoBase {
    public:
     virtual void formel() = 0;
    };
    class Circle : public GeoBase;
    class Rectangle : public GeoBase;
    

    Wie kann man erst zur Laufzeit die Instanzierung bzw. den Konstruktor-Aufruf mit new binden?

    GeoBase *obj = new Circle(); // Konstruktor zur Compile-Zeit gebunden
    obj->formel();
    

    Das geht so jedenfalls nicht. Also bauen wir uns Fabriken dazu. Dazu brauchen wir eine abstrakte Fabrik:

    class GeoBaseFactory {
     virtual GeoBase* create() = 0;
    }
    

    Und dann die passenden Factories:

    class CircleFactory : public GeoBaseFactory{
     virtual GeoBase* create() { 
       return new Circle();
     }
    }
    class RectangleFactory : public GeoBaseFactory {
     virtual GeoBase* create() {
       return new Rectangle();
     }
    }
    

    Wir haben die Instanzierung bzw. Konstruktor-Aufruf lediglich an eine andere Stelle verschoben, weg von der im Programmablauf in die Factories:

    GeoBaseFactory *gbf = new CircleFactory(); 
    // ...
    GeoBase *obj =  gbf->create(); // Circle-Konstruktor nicht mehr im Programmablauf!
    

    Das eigentliche new Circle(); ist jetzt nicht mehr zur Compile-Zeit gebunden, sondern durch das abstrakte gbf->create() an die Laufzeit gebunden.

    Welche Fabrik wie oder warum erzeugt wird, ist jedem selbst überlassen. Ob du das anhand einer Konfigurations-Datei (ini, XML usw.), eines Scripts (Lua, AngelScript usw.), eines switch-case-Blocks oder direkt C++-Code-Aufruf machst, ist dir bzw. der Anforderung überlassen!

    void foo(GeoBaseFactory *gbf) {
     // Konstruktor-Aufruf wird zur Laufzeit gebunden:
     GeoBase *obj = gbf->create();
     obj->formel();
    }
    
    int main() {
    // Welche Factory? Hier über ein Switch-Case:
     switch(i) {
      case 0:
       foo(new CircleFactory());
      break;
      case 1:
       foo(new RectangleFactory());
      break;
     }
    }
    

    Etwas mehr dazu unter http://www.kharchi.eu/?p=885


Log in to reply