[JAVA] Verwenden Sie die Standardmethode der Schnittstelle ordnungsgemäß?

Einführung

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?

Schlechtes Beispiel

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.

So erkennen Sie eine schlechte Verwendung

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.

Richtiges Anwendungsbeispiel

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.

Ausnahmefall

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.

Zusammenfassung

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

Verwenden Sie die Standardmethode der Schnittstelle ordnungsgemäß?
Ist die von Ihnen verwendete Version von Elasticsearch mit Java 11 kompatibel?
Die Falle, die die Standardimplementierung der Java 8-Schnittstelle mit sich bringt
Informationen zum Standardverhalten der Bruchrundung mit java.text.NumberFormat
Sind Sie immer noch erschöpft von der Beispielvideosuche? Eine Schaltfläche zum Senden von FANZA-Videos an Slack, wenn diese gedrückt wird.
Führen Sie Docker in die Anwendung ein, die Sie erstellen
Verwenden Sie die Standardmethode der Schnittstelle ordnungsgemäß?
[Java] Der Abdeckungsbericht konnte nicht durch Kombinieren der Standardmethode der Cobertura + -Schnittstelle erstellt werden
Java-Vergleich mit der compareTo () -Methode
[Schienen 6] zerstören mit der Ressourcenmethode
Iterative Verarbeitung von Ruby mit jeder Methode (finde die Summe von 1 bis 10)
Versuchen Sie, || anstelle des ternären Operators zu verwenden
Was sind die aktualisierten Funktionen von Java 13
Vertiefte mein Verständnis der Zusammenführungsmethode
Erhalten Sie eine Fehlermeldung mit einer beliebigen Methode
Überprüfen Sie die Funktion der Schnittstelle über den Thread
[Java] Lesen Sie die Fehlermeldung richtig? [Lesen des Stack-Trace]
Wenn Sie Android Room verwenden und die Spaltendefinition ändern möchten
[Bestellmethode] Legen Sie die Reihenfolge der Daten in Rails fest
[Java] Behandlung von Java Beans in der Methodenkette
Die Reihenfolge der Java-Methodenmodifikatoren ist festgelegt
Wenn Sie die Methode außerhalb verwenden möchten
Ausgabe der Verwendung der Slice-Methode
Was sind die Vorteile von DI und Thymeleaf?
Ich habe versucht, den Profiler von IntelliJ IDEA zu verwenden
Argumente mit Standardwerten Nehmen Sie als Beispiel die Methode full_title des Rails-Lernprogramms
Welchen Mann magst du? Identifizieren von Liebeszielen anhand des Chain of Responsibility-Musters