2009-08-12 6 views
-1

Hej!Vereinfachte Konfig-Datei-gesteuerte Fabrik

Vorsicht das ist ein langer Post - wenn Sie leicht genervt sind besser überspringen Sie es. ;-)

Bei dem Projekt, an dem ich gerade arbeite, geht es darum, Spannungsmessungen verschiedener Sensoren zu lesen und den jeweiligen physikalischen Wert zu berechnen. Es gibt eine Anzahl von Kanälen, an denen jeweils ein anderer Sensor angebracht sein kann. Grundsätzlich ist die Art des Sensors bekannt, aber die Details wie Empfindlichkeit und Verzerrung können sehr unterschiedlich sein. Während für jeden Sensortyp eine parametrisierte Klasse existiert, werden die Parameter aus den Konfigurationsdateien gezogen.

Angenommen, es gibt die folgende Klassenhierarchie ist:

class Sensor 
{ 
public: 
    virtual double Calculate(const double &arg)=0; 
}; 
class PTCResistor 
{ 
    PTCResistor(XMLNode &node); 
    double Calculate(const double &arg); 
}; 
class Thermocouple 
{ 
    Thermocouple(XMLNode &node); 
    double Calculate(const double &arg); 
}; 

Die Hauptkonfigurationsdatei sieht wie folgt aus:

<channels> 
    <TurbineInletTemp sensor="Thermocouple.TypeK.xml" /> 
    <CylinderHeadTemp sensor="PTCResistor.PT500.xml" /> 
    ... 
</channels> 

Wie Sie ein Attribut Sensor sehen jeder Kanal-Tag hat einige ohter XML-Datei angeben welche Parameterinformation und die Art des Sensors. Es sieht etwa so aus:

<sensor class="PTCResistor"> 
    <param Rref="500" Tref="0"> 
    ... 
</sensor> 

Das Format dieser Referenz XML-Dateien variieren kann, jedoch wird durch den Konstruktor jeder abgeleiteten Klasse verstanden.

Beim Programmstart wird die Hauptkonfigurationsdatei analysiert und eine Factory-Klasse löst die Verknüpfung zu den XMLs auf, die die sensorspezifischen Informationen enthalten, untersucht das Klassen-Tag und ruft die entsprechenden Konstruktoren auf. Wie folgt aus:

string chnTITFile = rootNode.GetNode("TurbineInletTemp").GetAttribute("sensor"); 
XMLNode chnTITNode = XMLNode.parseFile(RootPath + chnTITFile,"sensor") 
string className = chnTITNode .GetAttribute("class"); 

if(className == "Thermocouple") { 
    Sensor* sensorTIT = new Thermocouple(chnTITNode); 
} 
else if(className = "PTCResistor") { 
    Sensor* sensorTIT = new PTCResistor(chnTITNode); 
} 

Dies ist die schlanksten Lösung, die ich mit in dem Sinn kam, dass es nur wenige Orte sind zu ändern, wenn eine neue abgeleitete Klasse hinzugefügt wird. Im Grunde schreibt man eine neue abgeleitete Klasse und fügt einen weiteren if-Zweig in der Fabrik hinzu. Leider beinhaltet dies viele String-Vergleiche. Da die Anzahl der Klassen und Kanäle ziemlich groß sein kann (und das Zielsystem schwach ist), bin ich besorgt.

Eine andere Möglichkeit wäre Hash der Klassennamen. Dies würde wahrscheinlich wie folgt aussehen:

Problem hier: müssen die enum die Hash-Werte enthalten pflegen.

Während ich diesen Code schreibe, fühle ich, dass der Prozess des Hinzufügens eines neuen Sensors ziemlich mühsam sein kann. Irgendwelche Ideen, wie man das Durcheinander reduzieren kann? Oder ist das schon das kleinste Übel, das man bekommen kann?

Danke fürs Lesen! Arne

EDIT1

Wie Neil Butter vorgeschlagen halte ich durch eine Funktion wie

static Sensor* Thermocouple::Create(XMLNode &node) { 
    return new Thermocouple(node);  
    } 

jede Klasse zu erweitern und eine Hash-Tabelle erstellen, die den Namen der Klasse String in einen Funktionszeiger auf diese bezieht Statische Funktion:

Damit kann das passende Sensorobjekt für einen namentlich gekennzeichneten Kanal erstellt werden.Die Suche wird durch Hash-Vergleich durchgeführt und man muss nur die Map-Initialisierung im Konstruktor der Factory-Klasse beibehalten.

+0

Ich habe jede Fehlerüberprüfung absichtlich weggelassen, um den Code nicht noch mehr zu verwirren. Außerdem habe ich den obigen Code nicht zusammengestellt, da ich gerade nicht arbeite. – Arne

+0

classNameMap [Thermoelement] sollte classNameMap ["Thermocouple"] Ich denke - oder vielleicht geben Sie der Klasse eine statische Classname-Funktion, also dann becmes classNameMap [Thermocouple :: ClassName()] –

+0

ja, danke. scheint ich schlampig – Arne

Antwort

3

Dies ist das Factory-Muster - es gibt einen Wikipedia-Artikel here. Thre sind zig Möglichkeiten, dies umzusetzen, aber eine, die ich oft:

  • erstellen eine Objekthierarchie - jede Klasse hat eine statische Funktion erstellen, die eine Instanz der Klasse von seinen Parametern erstellen kann - die Parameter müssen gemeinsam sein, um alle Funktionen in der Hierarchie erstellen
  • Karte von Klassennamen erstellen Funktion eine neue Klasse für diese Klasse
  • Sie jedes Mal zum erstellen der Klassennamen Zeichenfolge und die Funktion anlegen mit der Karte registrieren hinzufügen
  • beim Parsen der XML, erhalten der Klassenname und verwenden Sie es dann für diese Klasse die auf die Create-Funktion suchen
  • Anruffunktion Erstellen Sie ein Objekt der erforderlichen Klasse

Die Funktionen nützlicherweise die Wurzel nehmen der XML-Entität erstellen erstellen beschreibt die Klasse als einen ihrer Parameter. Sie können dann das XML weiter zu Werten verarbeiten, die für die spezifische Objektkonstruktion benötigt werden.

+0

Vielen Dank für Ihren Kommentar. Ich habe den Artikel gelesen. Shure, der obige Code ist vielleicht nicht "genau" eine Fabrik, da die Konstruktoren öffentlich sind, aber es ist wirklich nicht das, was ich mit der Frage beziehe. Ich bin auf der Suche nach einer Möglichkeit, den Aufwand beim Hinzufügen neuer abgeleiteter Klassen zu reduzieren und dabei so leistungsfähig wie möglich in der Fabrik zu sein. – Arne

+0

Es ist wirklich einfach, eine neue Klasse hinzuzufügen. In dem Code, an dem ich gerade arbeite, ist es eine Zeile Code, um die Klasse und vielleicht 10 Zeilen Code zu registrieren, um die Create-Funktion zu schreiben. Und wenn Sie XML analysieren, ist die Leistung kein Problem. –

+0

So fügen Sie eine statische Funktion in jeder abgeleiteten Klasse hinzu und erstellen eine Zuordnung, die eine Zeichenfolge mit einem Funktionszeiger auf diese statische Funktion verknüpft? – Arne

1

Ein Muster besteht darin, dass Ihre abgeleiteten Klassen sich während der statischen Initialisierung bei der Factory registrieren, sodass Sie eine Liste aller abgeleiteten Klassen erstellen. Wenn Sie Ihre XML-Datei analysieren, suchen Sie in der Datenstruktur nach der Zeichenfolge, um die richtige zu verwendende abgeleitete Implementierung zu ermitteln.

+0

Danke für Ihren Kommentar! Können Sie erklären, wie dieser Registrierungsprozess durchgeführt wird? – Arne