Ich weiß, dass die Leute viel damit anfangen, Ausnahmen für die Flusskontrolle zu verwenden, aber das ist hier nicht das größte Problem. Ich sehe eine große Verletzung bei der Trennung von Befehlsabfragen . Aber auch das ist nicht das Schlimmste.
Nein, das Schlimmste ist genau hier:
/**
* Closes the door if open
*/
Warum zum Teufel explodiert alles andere, wenn Ihre Annahme über den Türzustand falsch ist, es aber lock()
nur für Sie behebt? Vergessen Sie, dass dies das Öffnen der Tür unmöglich macht, was absolut möglich und gelegentlich nützlich ist. Nein, das Problem hier ist, dass Sie zwei verschiedene Philosophien gemischt haben, um mit falschen Annahmen umzugehen. Das ist verwirrend. Tu das nicht. Nicht auf derselben Abstraktionsebene mit demselben Namensstil. Au! Nehmen Sie eine dieser Ideen mit nach draußen. Die Türservice-Methoden sollten alle gleich funktionieren.
Was die Verletzung der Befehlsabfragetrennung betrifft, sollte ich nicht versuchen müssen, eine Tür zu schließen, um herauszufinden, ob sie geschlossen oder offen ist. Ich sollte nur fragen können. Der Türservice bietet keine Möglichkeit, dies zu tun, ohne möglicherweise den Zustand der Tür zu ändern. Das macht dies so viel schlimmer als Befehle, die auch Werte zurückgeben (ein häufiges Missverständnis dessen, worum es bei CQS geht). Hier sind Zustandsänderungsbefehle die einzige Möglichkeit, Abfragen durchzuführen! Au!
Ausnahmen, die teurer sind als Statuscodes, sind Optimierungsgespräche. Schnell genug ist schnell genug. Nein, das eigentliche Problem ist, dass Menschen in typischen Fällen keine Ausnahmen erwarten. Sie können darüber streiten, was typisch ist, was Sie wollen. Für mich ist die große Frage, wie lesbar Sie den Verwendungscode machen.
ensureClosed(DoorService service, Door door){
// Need door closed and unlocked. No idea of its state. What to do?
try {
service.open(door)
service.close(door)
}
catch( DoorLockedException e ){
//Have no way to unlock the door so give up and die
log(e);
throw new NoOneGaveMeAKeyException(e);
}
catch( DoorAlreadyOpenedException e ){
try {
service.close(door);
}
catch( DoorAlreadyClosedException e ){
//Some multithreaded goof has been messing with our door.
//Oh well, this is what we wanted anyway.
//Hope they didn't lock it.
}
}
}
Bitte lassen Sie mich keinen solchen Code schreiben. Bitte geben Sie uns isLocked()
und isClosed()
Methoden. Mit denen kann ich meine eigenen ensureClosed()
und ensureUnlocked()
leicht lesbaren Methoden schreiben . Diejenigen, die nur werfen, wenn ihre Postbedingungen verletzt werden. Ich würde lieber nur feststellen, dass Sie sie natürlich bereits geschrieben und getestet haben. Mischen Sie sie nur nicht mit denen, die werfen, wenn sie den Zustand nicht ändern können. Geben Sie ihnen zumindest unterscheidende Namen.
Was auch immer Sie tun, rufen Sie bitte nichts an tryClose()
. Das ist ein schrecklicher Name.
Was das DoorLockedException
Alleinsein betrifft, muss DoorAlreadyLockedException
ich auch sagen: Es geht nur um die Verwendung von Code. Entwerfen Sie solche Dienste nicht, ohne den verwendeten Code zu schreiben und sich das Chaos anzusehen, das Sie erstellen. Refactor und Redesign, bis der verwendete Code mindestens lesbar ist. In der Tat sollten Sie zuerst den Verwendungscode schreiben.
ensureClosed(DoorService service, Door door){
if( !service.isClosed(door) ){
try{
service.close(door);
}
catch( DoorAlreadyClosedException e ){
//Some multithreaded goof has been messing with our door.
//Oh well, this is what we wanted anyway.
//Hope they didn't lock it.
}
} else {
//This is what you wanted, so quietly do nothing.
//Why are you even here? Who bothers to write empty else conditions?
}
}
ensureUnlocked(DoorService service, Door door){
if( service.islocked(door) ){
throw new NoOneGaveMeAKeyException();
}
}