Mit Java 8 wurde die Standardmethode der Schnittstelle eingeführt, und mit Java 9 kann auch eine private Methode für die Schnittstelle definiert werden. Dies ist eine nützliche Funktion, aber ein Missbrauch kann die Wartbarkeit Ihrer Anwendung beeinträchtigen. Welche Art der Nutzung ist also wünschenswert?
public interface BadUsage {
void someMethod();
// (1)Utility-like für Mix-In und Implementierungsvererbung
default String normalize(String str) {
return str.toUpperCase().trim();
}
// (2)Verarbeitung, die von anderen Objekten abhängt
default int getSomeValue() {
return Singleton.getInstance().getValue();
}
}
(1) des obigen Beispielcodes verwendet die Standardmethode zum Hinzufügen von Funktionen als Mix-In zu der Klasse, die die Schnittstelle implementiert, oder zum Bereitstellen von Dienstprogrammfunktionen, die üblicherweise auf der Seite der Implementierungsklasse verwendet werden können. Ich werde. "Übertragung statt Vererbung" ist eine objektorientierte ironclad-Regel. Vermeiden Sie diese Verwendung am besten und verwenden Sie Dienstprogrammklassen und Hilfsklassen. (2) ähnelt (1) in Bezug auf die Verwendung, problematischer ist jedoch, dass es die Verarbeitung beschreibt, die von anderen Objekten abhängt. In diesem Beispiel greifen wir auf ein Singleton-Objekt zu, die Verarbeitung erfolgt jedoch wie folgt.
Wenn Sie einen solchen Prozess schreiben, wird der Komponententest sehr schwierig. Die Schnittstelle sollte in erster Linie nicht von der konkreten Klasse abhängen.
Die Standardmethode der Schnittstelle ist "public". Dies ist eine Operation, die dem Benutzer wie die abstrakte Methode zur Verfügung gestellt wird. Daher sollte die Standardmethode auch Teil der Verantwortung der Schnittstelle sein. Da das "Objekt" in der Objektorientierung eine Sammlung von "Daten" und "Verhalten" ist, sollte die Instanzmethode, die das Verhalten darstellt, Zugriff auf die Daten des Objekts gewähren (Instanzvariable).
Natürlich kann die Schnittstelle selbst keine Instanzvariablen haben, daher greift die Standardmethode indirekt auf die Instanzvariablen der Implementierungsklasse zu, indem sie andere abstrakte Methoden aufruft. Umgekehrt sollten Sie die Standardmethode in Frage stellen, die keine anderen abstrakten Methoden aufruft.
Angenommen, Sie haben die folgende Schnittstelle: In der Schnittstelle des "zusammengesetzten Musters" befinden sich "zusammengesetzt" und "Blatt" in der konkreten Klasse.
Component.java
public interface Component {
String getName();
int getPoint();
List<Component> getChildren();
boolean isLeaf();
}
Angenommen, Sie möchten dieser Schnittstelle eine Find-Methode hinzufügen, die eine Liste der Komponenten zurückgibt, die Ihren Kriterien entsprechen. Diese Methode kann als allgemeiner Prozess beschrieben werden, der nicht von der konkreten Klasse abhängt. Implementieren Sie sie daher als Standardmethode.
Component.java
default List<Component> find(Predicate<Component> p) {
return getChildren().stream().filter(p).collect(Collectors.toList());
}
Unabhängig davon, was die konkrete Klasse wirklich ist, sollte die Standardmethode daher ein allgemeines Verhalten implementieren, das universell wäre, wenn die Schnittstelle "Komponente" korrekt implementiert würde.
Es gibt Ausnahmefälle, in denen es akzeptabel ist (keine anderen abstrakten Methoden aufzurufen). In diesem Fall benötigen Sie anstelle einer abstrakten Basisklasse, die eine Standardimplementierung bereitstellt, nur eine Schnittstelle. Nehmen wir ein konkretes Beispiel. Angenommen, Sie möchten ein "Besuchermuster" einführen, das die zusammengesetzte Struktur scannt.
Visitor.java
public interface Visitor {
void visit(Composite composite);
void visit(Leaf leaf);
}
Component.java
public interface Component {
// ...
void accept(Visitor visitor);
}
Das Folgende ist ein Beispiel für eine konkrete Besucherimplementierungsklasse.
LeafCounterVisitor.java
public class LeafCounterVisitor implements Visitor {
private int count = 0;
@Override
public void visit(Composite composite) {
//Ich bin nicht an zusammengesetzten Knoten interessiert, also leere Implementierung
}
@Override
public void visit(Leaf leaf) {
count ++;
}
public int getCount() {
return count;
}
}
Besuche an Knoten, die für den Besucher nicht von Interesse sind, sind leere Implementierungen, wie oben beschrieben. Diesmal gibt es nur zwei Arten von Knoten. Wenn es jedoch zehn gibt, kann es eine entmutigende Aufgabe sein, alle Besuchsmethoden zu überschreiben und eine leere Implementierung einzufügen. In diesem Fall gibt es eine Möglichkeit, eine abstrakte Basisklasse mit einer leeren Implementierung vorzubereiten, die wie unten gezeigt im Voraus vorbereitet wurde, und diese Klasse zu erben. (In abgeleiteten Klassen müssen Sie nur die Besuchsmethode von Interesse überschreiben.)
BaseVisitor.java
public abstract class BaseVisitor implements Visitor {
@Override
public void visit(Composite composite) {
//Leere Implementierung
}
@Override
public void visit(Leaf leaf) {
//Leere Implementierung
}
}
Die Standardmethode der Schnittstelle macht die obige abstrakte Basisklasse überflüssig.
Visitor.java(Geänderte Version)
public interface Visitor {
default void visit(Composite composite) {}
default void visit(Leaf leaf) {}
}
Ich denke, dass eine solche Verwendung nicht von der ursprünglichen Absicht der Standardmethode in dem Sinne abweicht, dass sie eine Standardimplementierung bietet.
Die Standardmethode der Schnittstelle sollte wie jede andere abstrakte Methode behandelt und wie ein objektorientiertes Design verwendet werden, außer dass es eine Standardimplementierung gibt.
Recommended Posts