Unser Wissensgebiet umfasst Menschen, die mit bloßen Füßen über eine Druckplatte laufen. Wir machen eine Bilderkennung, die Objekte der Klasse 'Fuß' ergibt, wenn ein menschlicher Fuß in den Sensordaten erkannt wird.
Es gibt verschiedene Berechnungen, die an den Daten des Fußes durchgeführt werden müssen.
Nun, welche API wäre besser:
class Foot : public RecognizedObject {
MaxPressureFrame getMaxPressureFrame();
FootAxis getFootAxis();
AnatomicalZones getAnatomicalZones();
// + similar getters for other calculations
// ...
}
Oder:
class Foot : public RecognizedObject {
virtual CalculationBase getCalculation(QString aName);
// ...
}
Nun gibt es eine Menge Vor- und Nachteile, die ich mir einfallen lassen kann, aber ich kann mich nicht wirklich entscheiden, welche die wichtigsten sind. Hinweis: Dies ist eine Endbenutzeranwendung, keine von uns verkaufte Softwarebibliothek.
Irgendein Rat?
Einige Profis für den ersten Ansatz könnten sein:
- KISS - alles ist sehr konkret. Die API, aber auch die Implementierung.
- stark typisierte Rückgabewerte.
- Das Erben von dieser Klasse ist narrensicher. Nichts kann überschrieben, nur hinzugefügt werden.
- API ist sehr geschlossen, nichts geht rein, nichts kann überschrieben werden, so dass weniger schief gehen kann.
Einige Con's:
- Die Anzahl der Getter wird zunehmen, da jede neue Berechnung, die wir erfinden, zur Liste hinzugefügt wird
- Es ist wahrscheinlicher, dass sich die API ändert. Wenn wichtige Änderungen vorgenommen werden, benötigen wir eine neue API-Version, Foot2.
- Im Falle der Wiederverwendung der Klasse in anderen Projekten benötigen wir möglicherweise nicht jede Berechnung
Einige Profis für den zweiten Ansatz:
- flexibler
- Es ist weniger wahrscheinlich, dass sich die API ändert (vorausgesetzt, wir haben die richtige Abstraktion, wenn nicht, kostet eine Änderung mehr).
Einige Con's:
- lose getippt. Muss bei jedem Anruf besetzt werden.
- der String-Parameter - ich habe schlechte Gefühle darüber (Verzweigung auf String-Werte ...)
- Es gibt keinen aktuellen Anwendungsfall / keine aktuelle Anforderung, die die zusätzliche Flexibilität vorschreibt, aber möglicherweise in der Zukunft.
- Die API legt Einschränkungen fest: Jede Berechnung muss von einer Basisklasse abgeleitet werden. Das Erhalten einer Berechnung wird durch diese 1 Methode erzwungen, und das Übergeben zusätzlicher Parameter wird unmöglich sein, es sei denn, wir entwickeln eine noch dynamischere, superflexible Methode zum Übergeben von Parametern, die die Komplexität noch weiter erhöht.
enum
und seine Werte einschalten. Trotzdem denke ich, dass die zweite Option böse ist, weil sie von KISS abweicht.getCalculation()
.Antworten:
Ich denke im ersten Ansatz wird sich auszahlen. Magische Zeichenfolgen können die folgenden Probleme verursachen: Tippfehler, Missbrauch, nicht triviale Sicherheit beim Zurückgeben, fehlende Code-Vervollständigung, unklarer Code (hatte diese Version diese Funktion? Ich denke, wir werden es zur Laufzeit herausfinden). Durch die Verwendung von Aufzählungen werden einige dieser Probleme behoben. Sehen wir uns jedoch die Nachteile an, die Sie angesprochen haben:
Das kann zwar ärgerlich sein, hält aber die Dinge schön und streng und gibt Ihnen überall in Ihrem Projekt in einer modernen IDE Code-Vervollständigung mit guten Überschriften-Kommentaren, die viel nützlicher sind als Aufzählungen.
Stimmt, aber das ist eigentlich ein riesiger Profi;) Sie können Schnittstellen für Teil-APIs definieren und müssen dann keine abhängigen Klassen neu kompilieren, die nicht von neueren APIs betroffen sind (also kein Foot2 erforderlich). Das ermöglicht eine bessere Entkopplung, die Abhängigkeit ist jetzt von der Schnittstelle und nicht von der Implementierung. Darüber hinaus tritt bei Änderungen an einer vorhandenen Schnittstelle in abhängigen Klassen ein Kompilierungsfehler auf. Dies ist hilfreich, um veralteten Code zu vermeiden.
Ich verstehe nicht, wie die Verwendung von magischen Zeichenfolgen oder Aufzählungen dabei helfen wird. Wenn ich das richtig verstehe, fügen Sie entweder den Code in die Foot-Klasse ein oder unterteilen ihn in ein paar kleinere Klassen. Dies gilt für beide Optionen
quelle
IEnumerable<T>.Count
), kann Code mit einem solchen Ansatz die Leistungsvorteile von new nutzen Schnittstellenfunktionen bei Verwendung von Implementierungen, die diese unterstützen, aber mit alten Implementierungen kompatibel bleiben.Ich würde Option 3 empfehlen: Stellen Sie klar, dass die Berechnungen kein wesentlicher Bestandteil der Abstraktion von a sind
Foot
, sondern darauf basieren . Dann können SieFoot
die Berechnungen wie folgt in separate Klassen aufteilen :Auf diese Weise haben Sie immer noch eine starke Typisierung Ihrer Berechnung und können neue hinzufügen, ohne den vorhandenen Code zu beeinträchtigen (es sei denn, Sie haben einen schwerwiegenden Fehler bei den von bereitgestellten Betragsinformationen gemacht
Foot
).quelle