Im Moment hadere ich mit einigen Benennungen und Details einiger interfaces.
Angenommen, man will interfaces definieren, die im wesentlichen dem Zweck dienen, Dingen einen sprechenderen Namen zu geben. Ein Beispiel wäre, dass man verschiedenen Objekttypen wie z.B. Customer
oder Product
(oder auch etwas generischem) irgendeinen „Messwert“ zuordnen will. Jetzt könnte man sowas erstellen wie
interface CustomerMeausure { double evaluateCustomer(Customer c); }
interface ProductMeausure { double evaluateProduct(Product p); }
interface ObjectMeausure<T> { double evaluateObject(T t); }
Die sind natürlich strukturell gleich, und mit Lambdas kann man die recht bequem mit Leben füllen. Aber mir stellen sich einige Fragen.
1. Spezifische Methodennamen?
Die wichtigste ist: Sollten auch die Methodennamen so spezifisch sein? Das wirkt irgendwie redundant und über-speziell. Man könnte auch einfach sagen
interface CustomerMeausure { double evaluate(Customer c); }
interface ProductMeausure { double evaluate(Product p); }
interface ObjectMeausure<T> { double evaluate(T t); }
Und ich sehe keinen Grund, das nicht zu tun, außer dem, dass es Probleme geben kann, wenn eine Klasse mehrere solcher Interfaces implementieren will. Das ist aber ein recht „schwaches“ Argument, weil man das ja i.a. nicht muss, und so eine Klasse sowieso nicht sichtbar sein sollte. Bei der Verwendung wird die Aussagekraft meistens über den Variablennamen abgehandelt. Sowas wie customerMeasure.evaluate(c)
finde ich OK - da muss nicht unbedingt customerMeasure.evaluateCustomer(c)
stehen.
Gibt es weitere Argumente, die
- dafür spechen, den Methodennamen möglichst spezifisch zu machen, oder
- dagegen spechen, ihn so allgemein zu machen, wie in dem Beispiel?
2. Vererbung bei eigenen Interfaces
Sollte man da eine Vererbung reinziehen, und die interfaces explizit als Spezialisierungen modellieren?
Das hängt natürlich von der Antwort auf 1. ab, und teilweise von der Domäne - aber wenn man letzteres mal ignoriert: Wenn man den allgemeinen Methodennamen gewählt hat, kann man einfach sagen
interface ObjectMeausure<T> {
double apply(T t);
}
interface CustomerMeausure extends ObjectMeasure<Customer> {
// No additional methods
}
interface ProductMeausure extends ObjectMeasure<Product > {
// No additional methods
}
Wenn man die speziellen Methodennamen verwendet hat, könnte man die noch mit default
-Methoden auf den allgemeineren runterbrechen:
interface ProductMeausure extends ObjectMeasure<Product > {
default double evaluateProduct(Product p) {
return evaluateObject(p);
}
}
so dass man auch wieder einen Lambda-fähigen „single abstract method“-Typ hat
3. Vererbung von Standard-API-Interfaces
Sollte man solche interfaces, die strukturell gleich zu welchen aus der Standard-API sind, auch die aus der Standard-API erweitern lassen? Sowas wie die angedeutete ObjectMeasure<T>
ist ja das gleiche wie eine ToDoubleFunction<T>
. Also könnte man sagen
interface ObjectMeasure<T> extends ToDoubleFunction<T> {
default double evaluateObject(T t) {
return applyAsDouble(t);
}
}
Die Frage nach den Methodennamen in diesem Fall ist schon fast ein Detail, im Vergleich zu der Frage: In welche Richtung wird delegiert? Sollte es vielleicht genau umgekehrt sein…? :
interface ObjectMeasure<T> extends ToDoubleFunction<T> {
default double applyAsDouble(T t) {
return evaluateObject(t);
}
}
Irgendwie finde ich keine wirklich driftigen Argumente für die eine oder andere Antwort auf diese Fragen, aber irgendwie hab’ ich das Gefühl, dass das wichtig ist, und ich mir ggf. später denke: „Sh!t, das hätt’ ich genau anders machen sollen“.
Übersehe ich was?