Hallo
Eine Frage, die ich in ähnlicher Form schon an anderer Stelle zu diskutieren versucht hatte, und die ich jetzt (“inspiriert” von http://forum.byte-welt.net/threads/9915-Programmierstil-Objektmethoden-vs-Klassenmethoden und dem Beitrag http://forum.byte-welt.net/threads/10000-Interfaces?p=63290#post63290 ) nochmal stellen möchte:
Nach welchen Kriterien entscheidet man, ob man eine Methode in ein Interface aufnimmt? Zu dem Fall von Interfacemethoden vs. static Utility Methods wurde im oben verlinkten Thread schon einiges besprochen. Deswegen bezieht sich die Frage hier jetzt mal nur auf den Fall, wo man eine Methode entweder “direkt” in ein Interface aufnehmen kann, oder man ein “externes” Interface definiert, das die Methode enthält, aber die Objekte, um die es geht, übergeben bekommt.
Am konkreten Beispiel: Es gibt Comparable<T>
und Comparator<T>
. Eigentlich ist das Interface Comparable<T>
überflüssig: Die Funktionalität, die damit angeboten wird, kann auch durch eine konkrete Implementierung von Comparator<T>
angeboten werden.
Ähnliche Fragestellungen ergeben sich (für mich und andere) oft, z.B. auch beim Zeichnen von Objekten, etwa in Spielen. Sollte man eine entsprechende Methode in ein Interface aufnehmen…
interface PaintableGameObject {
void paintGameObject(Graphics2D g);
}
oder (im Stil von CellRenderern o.ä. bei Swing) ein Interface definieren wie
interface GameObjectPainter<T extends GameObject> {
void paintGameObject(Graphics2D g, T gameObject);
}
?
Bestimmte Kriterien scheinen zunächst offensichtlich. Wenn zum Zeichnen etwa private Eigenschaften des GameObjects benötigt werden, dann “muss” man die Methode direkt im GameObject-Interface anbieten - aber abgesehen davon, dass man das im Voraus nicht wissen kann, ist das nur ein Scheinargument, weil man schließlich auch OHNE das Interface ‘PaintableGameObject’ in eine Klasse eine passende Methode einfügen könnte, die dann vom entsprechenden GameObjectPainter aufgerufen werden kann:
class Player
{
...
void paintPlayerUsingPrivateData(Graphics g) { ... }
}
class PlayerPainter implements GameObjectPainter<Player>
{
@Override
public void paintGameObject(Graphics2D g, Player player)
{
player.paintPlayerUsingPrivateData(g);
}
}
Ein IMHO wichtiger Vorteil der “extern” festgelegten Funktionalität ist, dass es deutlich leichter ist, verschiedene Funktionalitäten zu verwenden, ohne dass man dafür verschiedene Implementierungen der Modellklassen braucht. Offensichtlich wird das z.B. beim Comparable/Comparator-Beispiel, wenn man die Objekte mal NICHT entsprechend ihrer “natürlichen” Ordnung sortieren will, sondern z.B. eben absteigend statt aufsteigend - mit einem [japi]Collections#reverseOrder[/japi]-Comparator ist das dann sehr leicht. Dazu kommt natürlich noch, dass man z.B. eine Klasse ‘Person’ nicht einfach ‘Comparable’ machen kann, weil man nicht weiß, ob sie Alphabetisch nach ihrem Nachnamen verglichen werden soll, oder nach ihrer Schuhgröße. Ich finde auch, dass es gut ist, wenn man sein Modell nicht mit Methoden zumüllt, die es evtl. nur für ganz spezifische Anwendungsszenarien benötigt (was man als eine Ausprägung von ‘separation of concerns’ betrachten könnte).
Der einzige Nachteil, den ich bei den “externen” Funktionalitäten sehe, ist, dass ihre Verwendung in bestimmten Konstellationen unübersichtlich werden könnte - aber ob das nun mehr oder weniger unübersichtlich wäre, als EIN Interface mit zu vielen Methoden, kann man wohl nicht pauschal sagen.
Habe ich irgendwelche schlagend-wichtigen Vor- oder Nachteile übersehen?