Welche Methoden kommen in Interfaces?

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?

Das geht auch in die Richtung Data-Driven vs Objektorientiert.

Zusammengefasst sind es zwei Ansätze:

1. Das Objekt implementiert die Interfaces und stellt selbst die Methoden bereit

Pro:
[ul]
[li]Man findet alle Operationen direkt an dem Objekt
[/li][li]Man kann Standardverhalten definieren
[/li][/ul]

Contra:
[ul]
[li]Führt leicht zu überfüllten Interfaces
[/li][li]Standardverhalten resultiert oft nur aus dem aktuellen Anwendungsfall
[/li][li]Ändert sich eines der Interfaces, muss sich das Objekt anpassen
[/li][/ul]

2. Das Objekt beeinhaltet nur die Daten, die Interfaces werden von anderen Klassen implementiert, die das Objekt verwenden.

Pro:
[ul]
[li]Sehr saubere Trennung von Daten und Operationen
[/li][li]Mehrere Implementierungen möglich (Comparable als Beispiel)
[/li][li]Datenklasse muss bei Änderungen nicht angefasst werden
[/li][/ul]

Contra:
[ul]
[li]Mögliche Operationen sind schwer herauszufinden (woher weiß man, dass ein Comparator für die Schuhgröße schon irgendwo exisitert?)
[/li][/ul]

Ich behaupte mal, wenn man sich seine Klassen in Bezug auf SOLID genauer anschaut, dass man eher bei der zweiten Version landen wird.

Gruß

Ich finde, dass diese Frage in Java schwierig bis gar nicht zu beantworten ist, da einige aus anderen Sprachen bekannte Modellierungsmöglichkeiten, wie extension methods oder type classes fehlen.

Ich finde die Fragestellung schon etwas merkwuerdig.
Aber das war bestimmt absicht :wink:

Intertfaces nutzt man dann, wenn man die Schnittstellen komplett von der Implementierung entkopppeln will.
Fruher hab ich das immer gemacht, einfach weil man sonst keine Mocks fuer Tests erzeugen konnte.
Heute mache ich das nur noch, wenn ich wirklich die Schnittstelle von der Implementierung entkoppeln will, „frueher“ war der einzige UseCase fuer die andere Implementierung die Tests.

Allerdings hat das alles rein gar nix mit static zu tun, zumindest nicht direkt.
Ich nutze static eigentlich so gut wie gar nicht mehr, wozu auch?
Wenn man ein DI Frameowrk nutzt bietet es IMHO keine Vorteile.

static kann auch „OK“ sein, so ohne Zustand und nur mit minimalen externen Abhaengigkeiten.
Wenn du irgendwo eine static Methode hast, die zB. immer in Dateisystem schreiben will, ist das nicht gut, speziell fuers testen.

Habe letzten wieder den Spruch gelesen „stop writing untestable code“, denke damit ist vieles gesagt.

@Firephoenix 

„Data Driven“ hat man fruher „prozedural“ genannt :wink:
Kann in bestimmten Situationen die bessere Loesung sein, voer allem wenn man Prozedural denkt, endet dass immer so… aber, wie gesagt, manchmal passt das besser.
Meistens aber bietet OO richtig eingesetzt mehr flexibiliaetund vor allem viel weniger Redundanz.
SOLID fuehrt uebrigens zu OO, nicht zu prozeduralem („Data Driven“) Design.

Um wieder zum Thema bzw. zum Beispiel zu kommen:
2D/3D passt IME immer besser ins prozedurale Weltbild.

Und jetzt die Quizfrage: Ist es objektorientierter eine Methode compareTo(Foo foo) in die Klasse **Foo **zu schreiben, als ein spezielles Objekt **FooComparator **dafür zu erstellen, welches eine Methode compare(Foo foo1, Foo foo2) anbietet? :slight_smile:

Der Comparator entspricht sicherlich auch irgendwo einem prozeduralem Ansatz, da er nur Daten verarbeitet und selber keine hält.
Von außen gesehen ist es aber ein schönes kleines Objekt mit klar definierten Schnittstellen und Verantwortlichkeiten.
:rolleyes:

Gruß

@maki : Ja, das hatte jetzt mit static nichts mehr zu tun. Die aufgelisteten Punkte bzw. die letzte Frage von @Firephoenix fasst es ja zusammen: Es ist beides gleichermaßen Objektorientiert, und es gibt Pros und Contras (die in der Auflistung im wesentlichen dual sind, und den schon genannten entsprechen).

Ich finde nur, dass die “externen” Interfaces mehr rein technische Vorteile haben. Dass man u.U. nicht weiß, dass es diese Interfaces gibt oder wo sie zu finden sind, sind eher nicht-technische Probleme, und dem könnte demnach auch mit nicht-technischen Mitteln entgegen gewirkt werden - angefangen bei der externen Dokumentation, Codebeispielen, JavaDocs mit entsprechenden Links drin, und inzwischen etablierten Konventionen, wie etwa, dass man in einer Klasse ‘Persons’ eben Dinge findet, die mit der Klasse ‘Person’ zu tun haben.

Die einzige Befürchtung wäre, dass man, wenn man das “konsequent” und systematisch anwendet, eine Methode wie

void doSomething(Person p)
{
    p.compareTo(other);
    print(p.createString());
    p.insertInto(table);
    p.doWhatever();
}

irgendwann eher so aussieht…

void doSomething(Person p, PersonComparator c, PersonStringCreator s, PersonIntoTableInserter t, PersonWhateverDoer w...) { ... }

Ich würde das nicht pauschal als schlecht oder nachteilhaft ansehen (rein teschnisch: eher im Gegenteil), aber dass es unkonventionell erscheint, war eben der Anlass für die Frage…

Ich finde dieses “Prozedural-OO” oder wie man das nennen will, gar nicht so nicht-objektorientiert oder prozedural, wie viele meinen oder es einem vergraueln wollen. Mir ist klar, dass es viele Verfechter von “in OOP stellt jedes Objekt seine eigenen Operationen bereit” gibt und daher die Verarbeitung durch Dritte verteufeln bzw. als Anti-Pattern verkaufen wollen, weil das an die prozedurale Programmierung erinnert und somit nicht dem Geiste von OOP entsprechen kann. Ich sage aber, das ist völliger Quatsch. Der ersten Logik nach muss jedes Objekt für alles, was man mit einem Objekt anstellen kann, eigene Methoden/Operationen bereitstellen und die gesamte Logik beinhalten. Das führt jedoch nicht nur zu viel zu fetten Objekten und oft auch Redundanzen, wodurch auch noch der Code sehr an Komplexität zunehmen kann, sondern macht auch einfach nicht immer Sinn. Ich habe solchen Code bereits gesehen (leider eine uralte Software aus der eigenen Firma :D) und die Anwendung ist weder wartbar, noch funktioniert da irgendwas richtig und fürt oft zu Deadlocks, langen Wartezeiten, jede Menge Coderedundanzen und und und. Die Idee war ja nicht schlecht und entspricht voll dem Gedanken von OOP, aber ist einfach “falsch” umgesetzt worden. Das wird bei uns immer als Negativbeispiel gezeigt, um zu zeigen, dass nicht alles Gold ist, was gläzt und man erst einmal seinen Kopf bemühen sollte anstatt jedem Trend blind zu folgen. Allein von der logischen Betrachtung her muss man sich doch eingestehen, dass es für jede Domain entsprechende Objekte geben muss, die ihre “eine” Aufgabe haben, die sie richtig und gut erledigen und mehr nicht. Alles, was darüber hinaus geht, ist unsinnig, überflüssig und auch designtechnisch nicht unbedingt klug, auch wenn es auf den ersten Blick das Arbeiten mit diesen Objekten leichter aussehen mag, weil man keine zusätzlichen Objekte braucht, über die die zusätzlichen Arbeitsschritte abgehandelt werden. Da OOP eigentlich eine Idee oder eine Möglichkeit sein soll, die (logische) Realität abzubilden, kann man da auch diverse Beispiele bringen. Zum Beispiel druckt sich ein Dokument nicht selbst, sondern ein Drucker druckt dieses Dokument. Ein Mensch ist nicht in der Lage Geld zu produzieren bzw. vom Konto abzuheben, wir müssen dazu zum Geldautomaten laufen. Rohstoffe können sich nicht selbst zum Endprodukt verarbeiten, sondern müssen in Fabriken von Maschinen verarbeitet und produziert werden. Und die Liste geht noch unendlich weiter.

Im Falle von Comparable und Comparator würde ich sagen, dass beides richtig ist und je nach Situation auch Sinn machen. Ich kann mich mit jemand anderem vergleichen, ein anderer kann sich mit mir vergleichen und ein Dritter kann mich und jemand anderen vergleichen, alles möglich, alles richtig, alles ok. Ich persönlich bevorzuge den Comparator, also Vergleichen über Dritte, da es bei mir immer nur zu Einzelfällen kommt, dass Vergleiche durchgeführt werden müssen und ich so die Objekte von unnötigem Code frei halten kann (zumal es sein kann, dass ich Objekte an verschiedenen Stellen entsprechend der verschiedenen Kontexte unterschiedlich vergleiche und dabei der Vergleich über das Objekt selbst gar nicht möglich wäre, außer über Strategy, aber das ist dann auch wieder eine Form von “über Dritte”). Aber wenn klar ist, dass ein Objekt verglichen werden können soll, dann macht das Implementieren von Comparable Sinn. Leider geht das nur, wenn man neue Klassen schreibt, bestehende lassen sich ja nicht nachträglich ergänzen (bei eigenem Code immer, aber das sei mal mit “neuen Klassen” abgedeckt) und da geht dann natürlich nur der Vergleich über Dritte, sprich externe Comparator-Implementierungen.

[QUOTE=Firephoenix]
Der Comparator entspricht sicherlich auch irgendwo einem prozeduralem Ansatz, da er nur Daten verarbeitet und selber keine hält.
Von außen gesehen ist es aber ein schönes kleines Objekt mit klar definierten Schnittstellen und Verantwortlichkeiten.
:rolleyes:[/QUOTE]

Der Comparator ist das, was man in Haskell eine “Typklasse” nennen würde (nur wird dir dort zu einem Typ die gewünschte Typklassen-Instanz “automatisch” gefunden, wenn vorhanden), und man hat dieser Sprache schon vieles vorgeworfen, nur nicht “prozedural” zu sein. Durch diesen Ansatz werden zum einen nicht unmittelbar zur “Kernfunktionalität” einer Klasse gehörende Eigenschaften entkoppelt (was interessiert es eine Autoklasse, wie sie geordnet wird), und zum anderen die Möglichkeit geschaffen, die Funktionalität erst dann zu implementieren, wenn sie gebraucht wird - selbst wenn die zu vergleichende Klasse in einer Fremdbibliothek steckt. In Java und Scala kommt noch die Flexibilität hinzu, mehrere “Typklassen” für einen Typ schreiben zu können (was gerade bei Comparator sehr sinnvoll ist) - [Haskell kann das wegen seines “Lookup-Automatismus” nicht, dort müssen Typklassen-Instanzen innerhalb eines Scopes eindeutig sein].

Typklassen lösen an manchen Stellen auch das Henne-Ei-Problem, da sie auch Methoden beinhalten können, die am Typ selber statisch oder Konstruktoraufrufe sein müssten (und damit von den üblichen Vererbungsmechanismen ausgeschlossen wären). Typisches Beispiel wäre ein Monoid:

public interface Monoid<A> {
   public A zero();
   public A plus(A x,A y);
}

Während man plus auch am Typen A haben könnte (als x.plus(y)), entspricht die zero-Methode (für das neutrale Element) einem No-Arg-Konstruktor von A, nur lässt sich dessen Vorhandensein in A selbst nicht erzwingen.

Insofern sehe ich in derartigen “typklassen-artigen” Interfaces wie es Comparator ist, auch im Sinne von OO eine sinnvolle Ergänzung.

[QUOTE=Landei]Der Comparator ist das, was man in Haskell eine “Typklasse” nennen würde (nur wird dir dort zu einem Typ die gewünschte Typklassen-Instanz “automatisch” gefunden, wenn vorhanden), und man hat dieser Sprache schon vieles vorgeworfen, nur nicht “prozedural” zu sein. Durch diesen Ansatz werden zum einen nicht unmittelbar zur “Kernfunktionalität” einer Klasse gehörende Eigenschaften entkoppelt (was interessiert es eine Autoklasse, wie sie geordnet wird), und zum anderen die Möglichkeit geschaffen, die Funktionalität erst dann zu implementieren, wenn sie gebraucht wird - selbst wenn die zu vergleichende Klasse in einer Fremdbibliothek steckt. In Java und Scala kommt noch die Flexibilität hinzu, mehrere “Typklassen” für einen Typ schreiben zu können (was gerade bei Comparator sehr sinnvoll ist) - [Haskell kann das wegen seines “Lookup-Automatismus” nicht, dort müssen Typklassen-Instanzen innerhalb eines Scopes eindeutig sein].

Typklassen lösen an manchen Stellen auch das Henne-Ei-Problem, da sie auch Methoden beinhalten können, die am Typ selber statisch oder Konstruktoraufrufe sein müssten (und damit von den üblichen Vererbungsmechanismen ausgeschlossen wären). Typisches Beispiel wäre ein Monoid:

public interface Monoid<A> {
   public A zero();
   public A plus(A x,A y);
}

Während man plus auch am Typen A haben könnte (als x.plus(y)), entspricht die zero-Methode (für das neutrale Element) einem No-Arg-Konstruktor von A, nur lässt sich dessen Vorhandensein in A selbst nicht erzwingen.

Insofern sehe ich in derartigen “typklassen-artigen” Interfaces wie es Comparator ist, auch im Sinne von OO eine sinnvolle Ergänzung.[/QUOTE]

+1

OK. Auch OOP. “Eine sinnvolle Ergänzung”. Gibt es Kriterien, an denen man festmachen könnte, ob eine Methode in ein Interface gehört oder eher durch ein “externes” Interface angeboten werden sollte?

Bisher sehe ich so viele Vorteile für letzteres, dass ich in Erwägung ziehen muss, irgendwas wichtiges zu übersehen. Wer hat noch nicht schon mal (sinngemäß!!! natürlich NICHT genau so!) was geschrieben wie

public static boolean containsPersonWithSameName(Collection<Person> persons, Person otherPerson) { ... }
public static boolean containsPersonWithSameAge(Collection<Person> persons, Person otherPerson) { ... }

und sich gewünscht, stattdessen

persons.contains(otherPerson, personByNameEquality);
persons.contains(otherPerson, personByAgeEquality);

schreiben zu können? Tja, “equals” ist eben objektgebunden und fest vorgegeben. Mit einem externen Interface “EqualityRelation” (oder auch “ToStringCreator”, “HashcodeComputer”… alles analaog zu “Comparator”) hätte man viel mehr Freiheitsgrade…

[QUOTE=Firephoenix]Und jetzt die Quizfrage: Ist es objektorientierter eine Methode compareTo(Foo foo) in die Klasse **Foo **zu schreiben, als ein spezielles Objekt **FooComparator **dafür zu erstellen, welches eine Methode compare(Foo foo1, Foo foo2) anbietet? :slight_smile:

Der Comparator entspricht sicherlich auch irgendwo einem prozeduralem Ansatz, da er nur Daten verarbeitet und selber keine hält.
Von außen gesehen ist es aber ein schönes kleines Objekt mit klar definierten Schnittstellen und Verantwortlichkeiten.
:rolleyes:

Gruß[/QUOTE]
Hier die Quizantwort:
comparable wenn es um die Natuerliche Ordnung geht (zB. Zahlen, Money), comparator fuer die anderen (zB. String vergleichen und dabei die Gross-/Kleinsschreibung ignorien).

Uebrigens, Comparator an sich ist schon auch OO.

Mal ein ganz einfaches Beispiel:
JavaBeans sind nicht OO, sondern Datenstrukturen.
Die Operationen die auf diesen Daten arbeiten sind irgendwo anders im System verteilt.

Könnte man diese Antwort auf das angedeutete PaintableGameObject / GameObjectPainter übertragen?

Ich kann mir z.B. kaum einen Fall vorstellen, in dem ich es in Betracht ziehen würde, eine Methode wie

public static <T extends Comparable> doSomething(List<T> list) { ... }

zu schreiben. Sowas wie

public static <T> doSomething(List<T> list, Comparator<T> comparator) { ... }

ist IMMER allgemeiner und flexibler und stellt weniger Anforderungen an die übergebenen Objekte.

[QUOTE=Marco13]OK. Auch OOP. “Eine sinnvolle Ergänzung”. Gibt es Kriterien, an denen man festmachen könnte, ob eine Methode in ein Interface gehört oder eher durch ein “externes” Interface angeboten werden sollte?

Bisher sehe ich so viele Vorteile für letzteres, dass ich in Erwägung ziehen muss, irgendwas wichtiges zu übersehen. Wer hat noch nicht schon mal (sinngemäß!!! natürlich NICHT genau so!) was geschrieben wie

public static boolean containsPersonWithSameName(Collection<Person> persons, Person otherPerson) { ... }
public static boolean containsPersonWithSameAge(Collection<Person> persons, Person otherPerson) { ... }

und sich gewünscht, stattdessen

persons.contains(otherPerson, personByNameEquality);
persons.contains(otherPerson, personByAgeEquality);

schreiben zu können? Tja, “equals” ist eben objektgebunden und fest vorgegeben. Mit einem externen Interface “EqualityRelation” (oder auch “ToStringCreator”, “HashcodeComputer”… alles analaog zu “Comparator”) hätte man viel mehr Freiheitsgrade…[/QUOTE]

In Java 8 hast du sehr wahrscheinlich sowas:

persons.contains(person -> person.name().equals(otherPerson.name()));

Könnte man das (bzw., wenn man es als eine spezielle Form dessen ansieht, auch der allgemeine Übergang von externer zu interner Iteration, oder eine Ausprägung der zunehmenden Verbreitung von funkionalen Elementen in den Mainstream-Sprachen) als eine allgemeine Tendenz oder gar Richtschnur ansehen? Zumindest erscheint es mir so, dass wenn man vor der Wahl steht, eine Methode doSomething in ein Interface aufzunehmen, oder stattdessen ein interface SomethingDoer mit der analogen Methode zu erstellen, oft mehr für letzteres spricht…

Also ich für meinen Teil bin auch immer zwischen beiden Möglichkeiten hin und hergerissen…
Bei Datenstrukturen, die verschiedene Felder haben, die man vergleichen oder zwecks Suche indizieren möchte, bietet sich mMn schon so etwas:

persons.contains(otherPerson, personByAgeEquality);```und/oder soetwas
```persons.sort(personByNameEquality);
persons.sort(personByAgeEquality);```an, obgleich sich dafür dann auch eine Utility-Klasse wie z.B. Collections eignen würde.
Aber bei Datenstrukturen, wo der Inhalt nur dargestellt oder verändert werden soll (z.B. Bilder oder 3D-Objekte) und zwar ohne nähere Kenntnis darüber, wie diese Strukturen (Pixel-, Vertex-, Texturformate) aufgebaut sind, erwische ich mich immer dabei, mir von solchen Strukturen einen "SomethingDoer" zu beschaffen, bei Bildern wäre dies z.B. das Graphics2D-Objekt und etwas ähnliches plane ich nun auch für 3D-Objekte. Die Idee lag eigentlich schon länger offenkundig rum, nur bin ich erst kürzlich selber drauf gekommen, dass es konkret 'ne Möglichkeit ist.
```interface Drawable3D {
  paint(Graphics3D g);
}

class Component3D implements Drawable3D {
  public void repaint() {
    Graphics3D g = ((Object3D) world).createGraphics3D();
    // drop Repaint3D-Event which calls
    paint(g);
  }

  public void paint(Graphics3D g) {
    g.setTransform(matrix);
    g.setMaterial(mat1);
    g.drawMesh(mesh1);
    // and/or quite simple
    g.drawObject(obj1);
  }
}```