Verwenden Sie auch nicht, wenn Sie dies vermeiden können. Verwenden Sie eine Factory-Funktion, die einen Optionstyp, ein Tupel oder einen dedizierten Summentyp zurückgibt. Mit anderen Worten, repräsentieren Sie einen potenziell standardmäßigen Wert mit einem anderen Typ als einen garantierten, nicht standardmäßigen Wert.
Lassen Sie uns zuerst die Desiderata auflisten und dann darüber nachdenken, wie wir dies in einigen Sprachen ausdrücken können (C ++, OCaml, Python).
- versehentliche Verwendung eines Standardobjekts zur Kompilierungszeit abfangen.
- Machen Sie beim Lesen des Codes deutlich, ob ein bestimmter Wert möglicherweise den Standardwert hat oder nicht.
- Wählen Sie unseren sinnvollen Standardwert, falls zutreffend, einmal pro Typ . Wählen Sie auf keinen Fall an jedem Anrufstandort einen potenziell anderen Standard aus .
- Erleichtern Sie statischen Analysewerkzeugen oder Menschen
grepdie Suche nach möglichen Fehlern.
- Bei einigen Anwendungen sollte das Programm normal fortgesetzt werden, wenn unerwartet ein Standardwert angegeben wird. Bei anderen Anwendungen sollte das Programm sofort angehalten werden, wenn ein Standardwert angegeben wurde, idealerweise auf informative Weise.
Ich denke, die Spannung zwischen dem Nullobjektmuster und den Nullzeigern kommt von (5). Wenn wir Fehler jedoch früh genug erkennen können, wird (5) streitig.
Betrachten wir diese Sprache nach Sprache:
C ++
Meiner Meinung nach sollte eine C ++ - Klasse im Allgemeinen standardmäßig konstruierbar sein, da sie die Interaktion mit Bibliotheken erleichtert und die Verwendung der Klasse in Containern erleichtert. Dies vereinfacht auch die Vererbung, da Sie nicht darüber nachdenken müssen, welcher Superklassenkonstruktor aufgerufen werden soll.
Dies bedeutet jedoch, dass Sie nicht sicher wissen können, ob sich ein Wert vom Typ MyClassim "Standardzustand" befindet oder nicht. Abgesehen davon, dass Sie bool nonemptyzur Laufzeit ein oder ein ähnliches Feld für die Oberflächen-Standardisierung eingeben, können Sie neue Instanzen von so erstellen MyClass, dass der Benutzer gezwungen ist, dies zu überprüfen .
Ich würde empfehlen, eine Factory-Funktion zu verwenden, die einen oder, wenn möglich std::optional<MyClass>, std::pair<bool, MyClass>einen r-Wert-Verweis auf einen zurückgibt std::unique_ptr<MyClass>.
Wenn Sie möchten, dass Ihre Factory-Funktion einen anderen "Platzhalter" -Zustand als den standardmäßig erstellten zurückgibt MyClass, verwenden Sie a, std::pairund vergewissern Sie sich, dass Ihre Funktion dies tut.
Wenn die Werksfunktion einen eindeutigen Namen hat, ist es einfach, grepnach dem Namen zu suchen und nach Schlampigkeit zu suchen. Es ist jedoch schwierig, grepFälle zu ermitteln, in denen der Programmierer die Werksfunktion hätte verwenden sollen, dies jedoch nicht tat .
OCaml
Wenn Sie eine Sprache wie OCaml verwenden, können Sie einfach einen optionTyp (in der OCaml-Standardbibliothek) oder einen eitherTyp (nicht in der Standardbibliothek, aber einfach zu rollen) verwenden. Oder ein defaultableTyp (ich erfinde den Begriff defaultable).
type 'a option =
| None
| Some of 'a
type ('a, 'b) either =
| Left of 'a
| Right of 'b
type 'a defaultable =
| Default of 'a
| Real of 'a
Ein defaultablewie oben gezeigtes ist besser als ein Paar, da der Benutzer eine Musterübereinstimmung durchführen muss, um das zu extrahieren, 'aund das erste Element des Paares nicht einfach ignorieren kann.
Das defaultableoben gezeigte Äquivalent des Typs kann in C ++ unter Verwendung von a std::variantmit zwei Instanzen desselben Typs verwendet werden, Teile der std::variantAPI können jedoch nicht verwendet werden, wenn beide Typen identisch sind. Es ist auch eine seltsame Verwendung, std::variantda seine Typkonstruktoren unbenannt sind.
Python
Sie erhalten ohnehin keine Überprüfung zur Kompilierungszeit für Python. Bei einer dynamischen Typisierung gibt es jedoch im Allgemeinen keine Umstände, unter denen Sie eine Platzhalterinstanz eines bestimmten Typs benötigen, um den Compiler zu platzieren.
Ich würde nur empfehlen, eine Ausnahme auszulösen, wenn Sie gezwungen wären, eine Standardinstanz zu erstellen.
Wenn dies nicht akzeptabel ist, würde ich empfehlen, ein zu erstellen DefaultedMyClass, MyClassdas von Ihrer werkseitigen Funktion erbt, und dieses zurückzusenden. Das verschafft Ihnen Flexibilität in Bezug auf die "Stubbing" -Funktionalität in Standardinstanzen, wenn Sie dies benötigen.