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
grep
die 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 MyClass
im "Standardzustand" befindet oder nicht. Abgesehen davon, dass Sie bool nonempty
zur 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::pair
und vergewissern Sie sich, dass Ihre Funktion dies tut.
Wenn die Werksfunktion einen eindeutigen Namen hat, ist es einfach, grep
nach dem Namen zu suchen und nach Schlampigkeit zu suchen. Es ist jedoch schwierig, grep
Fä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 option
Typ (in der OCaml-Standardbibliothek) oder einen either
Typ (nicht in der Standardbibliothek, aber einfach zu rollen) verwenden. Oder ein defaultable
Typ (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 defaultable
wie oben gezeigtes ist besser als ein Paar, da der Benutzer eine Musterübereinstimmung durchführen muss, um das zu extrahieren, 'a
und das erste Element des Paares nicht einfach ignorieren kann.
Das defaultable
oben gezeigte Äquivalent des Typs kann in C ++ unter Verwendung von a std::variant
mit zwei Instanzen desselben Typs verwendet werden, Teile der std::variant
API können jedoch nicht verwendet werden, wenn beide Typen identisch sind. Es ist auch eine seltsame Verwendung, std::variant
da 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
, MyClass
das 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.