Die Aufgabe besteht darin, eine Hardware innerhalb des Geräts gemäß einer Eingabespezifikation zu konfigurieren. Dies sollte wie folgt erreicht werden:
1) Sammeln Sie die Konfigurationsinformationen. Dies kann zu verschiedenen Zeiten und an verschiedenen Orten geschehen. Beispielsweise können Modul A und Modul B (zu unterschiedlichen Zeiten) einige Ressourcen von meinem Modul anfordern. Diese "Ressourcen" sind eigentlich die Konfiguration.
2) Nachdem klar ist, dass keine weiteren Anforderungen mehr realisiert werden, muss ein Startbefehl, der eine Zusammenfassung der angeforderten Ressourcen enthält, an die Hardware gesendet werden.
3) Erst danach kann (und muss) eine detaillierte Konfiguration der Ressourcen erfolgen.
4) Auch kann (und muss) erst nach 2) das Routing ausgewählter Ressourcen an die deklarierten Anrufer erfolgen.
Eine häufige Ursache für Fehler, auch für mich, der das Ding geschrieben hat, ist das Verwechseln dieser Reihenfolge. Welche Namenskonventionen, Designs oder Mechanismen kann ich verwenden, um die Benutzeroberfläche für jemanden nutzbar zu machen, der den Code zum ersten Mal sieht?
quelle
discovery
oderhandshake
?Antworten:
Es ist eine Neugestaltung, aber Sie können den Missbrauch vieler APIs verhindern, wenn Sie keine Methode zur Verfügung haben, die nicht aufgerufen werden sollte.
Zum Beispiel anstelle von
first you init, then you start, then you stop
Ihr Konstruktor ist
init
ein Objekt, das gestartet werden kann, undstart
erstellt eine Sitzung, die gestoppt werden kann.Wenn Sie sich auf eine Sitzung gleichzeitig beschränken, müssen Sie natürlich den Fall behandeln, in dem jemand versucht, eine Sitzung mit einer bereits aktiven Sitzung zu erstellen.
Wenden Sie diese Technik nun auf Ihren eigenen Fall an.
quelle
zlib
undjpeglib
sind zwei Beispiele, die diesem Initialisierungsmuster folgen. Dennoch sind zahlreiche Dokumentationen erforderlich, um Entwicklern das Konzept beizubringen.Sie können die Startmethode veranlassen, ein Objekt, das ein erforderlicher Parameter ist, an die Konfiguration zurückzugeben:
Selbst wenn es sich bei Ihrer
MySession
Struktur nur um eine leere Struktur handelt, wird durch die Typensicherheit sichergestellt, dassConfigure()
vor dem Start keine Methode aufgerufen werden kann.quelle
module->GetResource()->Configure(nullptr)
?a, b, c, d
, kann ich damit anfangena
undMySession
versuchen, esb
als bereits gestartetes Objekt zu verwenden, während dies in Wirklichkeit nicht der Fall ist.Aufbauend auf der Antwort von Cashcow - warum müssen Sie dem Aufrufer ein neues Objekt präsentieren, wenn Sie nur ein neues Interface präsentieren können? Rebrand-Pattern:
Sie können ITerminateable auch IRunnable implementieren lassen, wenn eine Sitzung mehrmals ausgeführt werden kann.
Ihr Objekt:
Auf diese Weise können Sie nur die richtigen Methoden aufrufen, da Sie am Anfang nur das IStartable-Interface haben und die run () -Methode nur dann aufrufen, wenn Sie start () aufgerufen haben. Von außen sieht es aus wie ein Muster mit mehreren Klassen und Objekten, aber die zugrunde liegende Klasse bleibt eine Klasse, auf die immer verwiesen wird.
quelle
Es gibt viele gültige Ansätze, um Ihr Problem zu lösen. Basile Starynkevitch schlug einen bürokratiefreien Ansatz vor, bei dem Sie eine einfache Schnittstelle haben und sich darauf verlassen können, dass der Programmierer die Schnittstelle entsprechend verwendet. Während ich diesen Ansatz mag, werde ich einen anderen vorstellen, der mehr Ingenieurskunst hat, aber dem Compiler erlaubt, einige Fehler abzufangen.
Identifizieren Sie die verschiedenen Zustände Ihr Gerät in kann, wie
Uninitialised
,Started
,Configured
und so weiter. Die Liste muss endlich seinFür jeden Zustand, definiert ein
struct
zu diesem Zustand hält die erforderlichen zusätzliche relevante Informationen, zum BeispielDeviceUninitialised
,DeviceStarted
und so weiter.Packen Sie alle Behandlungen in ein Objekt,
DeviceStrategy
wobei die Methoden die in 2. definierten Strukturen als Ein- und Ausgänge verwenden. So haben Sie möglicherweise eineDeviceStarted DeviceStrategy::start (DeviceUninitalised dev)
Methode (oder was auch immer die Entsprechung gemäß Ihren Projektkonventionen sein könnte).Bei diesem Ansatz muss ein gültiges Programm einige Methoden in der von den Methodenprototypen erzwungenen Reihenfolge aufrufen.
Die verschiedenen Zustände sind nicht verwandte Objekte, dies liegt am Substitutionsprinzip. Wenn es für Sie nützlich ist, wenn diese Strukturen einen gemeinsamen Vorfahren haben, erinnern Sie sich daran, dass das Besuchermuster verwendet werden kann, um den konkreten Typ der Instanz einer abstrakten Klasse wiederherzustellen.
Während ich in 3. eine einzigartige
DeviceStrategy
Klasse beschrieben habe, gibt es Situationen, in denen Sie die bereitgestellten Funktionen auf mehrere Klassen aufteilen möchten.Zusammenfassend sind die wichtigsten Punkte des von mir beschriebenen Designs:
Aufgrund des Substitutionsprinzips sollten Objekte, die Gerätezustände darstellen, unterschiedlich sein und keine speziellen Vererbungsbeziehungen aufweisen.
Packen Sie Gerätebehandlungen in Startegy-Objekte und nicht in Objekte, die Geräte selbst darstellen, sodass jedes Gerät oder jeder Gerätezustand nur sich selbst sieht und die Strategie alle von ihnen sieht und mögliche Übergänge zwischen ihnen ausdrückt.
Ich würde schwören, dass ich einmal eine Beschreibung einer Telnet-Client-Implementierung gesehen habe, die diesen Zeilen folgt, aber ich konnte sie nicht wiederfinden. Es wäre eine sehr nützliche Referenz gewesen!
¹: Folgen Sie dazu entweder Ihrer Intuition oder finden Sie in Ihrer konkreten Implementierung die Äquivalenzklassen von Methoden für die Beziehung „method₁ ~ method₂ iff. Es ist gültig, sie für dasselbe Objekt zu verwenden. “Vorausgesetzt, Sie haben ein großes Objekt, in dem alle Behandlungen auf Ihrem Gerät zusammengefasst sind. Beide Methoden zur Auflistung von Status liefern fantastische Ergebnisse.
quelle
Verwenden Sie ein Builder-Muster.
Haben Sie ein Objekt, das Methoden für alle oben genannten Operationen hat. Diese Vorgänge werden jedoch nicht sofort ausgeführt. Es merkt sich nur jede Operation für später. Da die Operationen nicht sofort ausgeführt werden, spielt die Reihenfolge, in der Sie sie an den Builder übergeben, keine Rolle.
Nachdem Sie alle Operationen im Builder definiert haben, rufen Sie eine
execute
-Methode auf. Wenn diese Methode aufgerufen wird, führt sie alle oben aufgeführten Schritte in der richtigen Reihenfolge mit den oben gespeicherten Vorgängen aus. Diese Methode ist auch ein guter Ort, um betriebsübergreifende Sicherheitsüberprüfungen durchzuführen (z. B. den Versuch, eine noch nicht eingerichtete Ressource zu konfigurieren), bevor Sie sie auf die Hardware schreiben. Dies erspart Ihnen möglicherweise die Beschädigung der Hardware mit einer unsinnigen Konfiguration (falls Ihre Hardware dafür anfällig ist).quelle
Sie müssen nur richtig dokumentieren, wie die Benutzeroberfläche verwendet wird, und ein Tutorial-Beispiel geben.
Möglicherweise haben Sie auch eine Debugging-Bibliotheksvariante, die einige Laufzeitprüfungen durchführt.
Vielleicht definieren und richtig gibt es Namenskonventionen (zB Dokumentation
preconfigure*
,startup*
,postconfigure*
,run*
....)Übrigens folgen viele vorhandene Schnittstellen einem ähnlichen Muster (z. B. X11-Toolkits).
quelle
Dies ist in der Tat eine häufige und heimtückische Art von Fehler, da Compiler nur Syntaxbedingungen erzwingen können, während Ihre Client-Programme "grammatikalisch" korrekt sein müssen.
Leider sind Benennungskonventionen gegen diese Art von Fehler fast völlig wirkungslos. Wenn Sie die Leute wirklich ermutigen möchten, nichts Unrammatisches zu tun, sollten Sie ein Befehlsobjekt verteilen, das mit Werten für die Voraussetzungen initialisiert werden muss, damit sie die Schritte nicht in falscher Reihenfolge ausführen können.
quelle
Mit diesem Muster können Sie sicher sein, dass jeder Implementierer genau in dieser Reihenfolge ausgeführt wird. Sie können noch einen Schritt weiter gehen und eine ExecutorFactory erstellen, die Executors mit benutzerdefinierten Ausführungspfaden erstellt.
quelle
step1(); step2(); step3();
. Der Point-of-Step-Builder besteht darin, eine API bereitzustellen, die einige Schritte verfügbar macht, und die Reihenfolge zu erzwingen, in der sie aufgerufen werden. Es sollte einen Programmierer nicht davon abhalten, zwischen den Schritten andere Dinge zu tun.