Thomas Bandt hatte sich per Twitter gewundert, warum Tests, die AssertWasCalled verwenden, fehlschlagen, wenn er den Typ eines Methodenargumentes von string zu einem eigenen Typ ändert. Ich antwortete darauf reflexartig (und korrekt) mit “Equals überschreiben”.
Das haben Thomas und Albert zum Anlass genommen, über die Problematik zu bloggen. Thomas hat meinen Vorschlag aufgegriffen und Equals überschrieben. Albert empfiehlt die Verwendung der statischen Args Klasse, um so einzelne Werte des Parameters zu prüfen. Alles schön und gut und hilfreich.
Was mich jedoch verwirrt hat, war Alberts Aussage “Für die Lösung musste er .Equals() überschreiben, nur für den Test.”. Um präzise zu sein: mich quälte die Frage, ob es tatsächlich “nur für den Test” erforderlich ist, Equals zu überschreiben. Implementierungsdetails die nur deshalb erforderlich sind, damit man einen Aspekt testen kann, sind auch mir erst mal suspekt.
Kontext hilft
Nun ist es natürlich schwer, die Frage nach der Sinnhaftigkeit der Equals Methode ohne Kontext zu diskutieren. Mein Kontext soll daher das Domain Driven Design (DDD) bilden. Im Kontext von DDD gibt es zwei unterschiedliche Arten von Klassen: Entities und Value Objects.

Entity
Entities sind Artefakte, die eine Identität haben. Selbst wenn alle Eigenschaften zweier Entities gleich sind, bedeutet das nicht, dass die Entities die selben sind. Das Kölner Telefonbuch listet seitenweise “Peter Müller”. Vorname und Nachname sind gleich, die Personen natürlich nicht. Sie haben eine Identität. Bei der Modellierung wird Entities in der Regel eine eindeutige Id zugeordnet. Ist die Id zweier Entity Objekte gleich, handelt es sich um das selbe Objekt.
In diesem Kontext ist es wichtig, die Equals Methode so zu definieren, dass Objekte mit gleicher Id (und gleichem Typ) als gleich angesehen werden.
Value Object
Im Beispiel von Thomas handelt es sich jedoch bei EmailRecipient um ein Value Object. Value Objects haben keine Identität. Das bedeutet, sobald die Eigenschaften zweier Value Objects gleich sind, gelten die beiden Objekte als gleich. Es ist wenig sinnvoll, zwei Emailadressen, deren string Repräsentation gleich ist, unterscheiden zu wollen. Auch zwei Integer Werte sind gleich, sobald ihr Wert gleich ist. Die eine 5 von der anderen 5 zu unterscheiden wäre nicht sinnvoll.
Im DDD Kontext wird daher bei Value Objects eine Equals Methode so implementiert, dass zwei Objekte mit gleichen Eigenschaften als gleich gelten. Genau das hat Thomas in seinem Beispiel getan: zwei EmailRecipients sind gleich, wenn alle Eigenschaften gleich sind.
Ich meine daher, die Equals Methode ist in diesem Kontext völlig zu Recht implementiert. Der Grund liegt nicht in der Testbarkeit sondern in der Tatsache, dass EmailRecipient eine Value Object Semantik erhalten soll.