it-swarm.com.de

Fruchtobj = neue Orange (); und Orange obj = new Orange (); Wenn beide in meinem Code identisch funktionieren, welches ist das weniger koppelnde?

Angenommen, ich habe 2 Klassen (die keine Methoden zeigen, um einfacher auszusehen):

public interface Fruit{
}

public class Orange implements Fruit{
}

und nehme an, ich kann Orange auf zwei Arten ohne tatsächlichen Unterschied initialisieren (bedeutet, dass beide in meinem Code auf dieselbe Weise kompiliert werden und arbeiten können):

1.Abstrakt eins:

Fruit obj=new Orange();

2. Konkrete:

Orange obj=new Orange();

, gemäß "Programmieren auf eine Schnittstelle" verstehen , so wie ich es verstehe, sollte ich die abstrakte verwenden, da sie mehr zu "Programmieren auf eine Schnittstelle" passt und weniger Kopplung aufweist.

Aber ich verstehe nicht ganz, warum das Abstrakte weniger Kopplung hat, weil ich denke, dass das Abstrakte 2 Schlüsselwörter von Klassen hat: Obst und Orange, während das Konkrete nur 1 Schlüsselwort hat: Orange, was bedeutet, dass das Abstrakte von mehr abhängt Klassen. Obwohl ich damit einverstanden bin, dass die Zusammenfassung es mir ermöglicht, von Orange zu einem anderen Typ (z. B. Traube) zu wechseln, indem weniger Zeichen gelöscht und eingegeben werden, enthält sie nach dem Wechsel immer noch zwei Schlüsselwörter (Obst und Traube).

Ich fand auch den anderen Nachteil des abstrakten, wenn Orange eines Tages die Schnittstelle entfernen oder zu einer anderen Schnittstelle wechseln muss:

public class Orange{
}

oder öffentliche Schnittstelle CircleShape {}

public class Orange implements CircleShape{
}

muss das Abstrakte modifiziert werden, während das Konkrete dies nicht tun muss. Ich denke also, der konkrete hat weniger Kopplung als der abstrakte. Ist das wahr? Wenn nicht, was ist hier das Missverständnis?

3
aacceeggiikk

Die Kopplung hier erfolgt zwischen Orange und dem Rest Ihres Programms. Wenn Sie eine Instanz von Orange beibehalten, können Sie möglicherweise eine Methode aufrufen, die für Orange und nicht für Fruit spezifisch ist. Dies ist aus zwei Gründen schlecht: A) Die Verwendung von Methoden, die für Orange spezifisch sind, bedeutet, dass Sie auf diese Instanz nicht einfach als Fruit -Instanz reagieren können (verstößt gegen das Liskov-Substitutionsprinzip), und B) impliziert dies "Orange" ist keine "Frucht", da sie sich anders verhält.

Angenommen, Sie benötigen eine neue Liste, und so tun Sie Folgendes:

ArrayList<String> list = new ArrayList<String>();

Sie entscheiden, dass Sie dies an eine Methode übergeben möchten, und so wird Ihre Methode natürlich:

public void handleList(ArrayList<String> list) {
    // ...
}

Wenn Sie darüber nachdenken, ist es der Methode handleList nicht besonders wichtig, dass es sich höchstwahrscheinlich um eine Instanz von ArrayList oder sogar List handelt. Es muss nur mindestens einmal mit jedem Objekt in dieser Sammlung behandelt werden. Sie können es also sicher wie folgt umschreiben und möglicherweise andere Implementierungen bestehen (Ihre eigenen eingeschlossen!):

public void handleList(Collection<String> list) {
    // ...
}

Wenn Sie noch einen Schritt weiter gehen möchten, verwenden Sie anstelle von String einen Platzhalter (?) Und rufen für jedes Element in der Sammlung toString () auf. Der Punkt ist, dass wir nicht verpflichtet sind, eine Instanz von ArrayList zu verwenden. Wir könnten stattdessen LinkedList oder HashSet oder TreeSet verwenden.

Zurück zu Ihrem Beispiel, idealerweise in Ihrem Programm, sollten Sie sich darum kümmern, dass es Orange im Gegensatz zu Grape oder Apple Klassen genau in eine Stelle in Ihrem Programm. Nach diesem Punkt sollten Sie diese Instanzen nur noch als Fruit verwenden, oder anders ausgedrückt, es ist Ihnen egal, wie es implementiert wird, es ist Ihnen nur wichtig, wie es verwendet wird .

Es kann jedoch zweckmäßig sein, eine abstrakte gemeinsame Klasse mit gemeinsamen Funktionen zwischen Implementierungen zu haben. Dies ist kein Ersatz für eine Schnittstelle. Eine Schnittstelle definiert, wie sie verwendet wird . Eine abstrakte Klasse soll Ihre Implementierungen reduzieren und vereinfachen. Möglicherweise haben Sie mehrere abstrakte Klassen mit jeweils mehreren konkreten Implementierungen, die alle von einer einzigen Schnittstelle stammen, die Sie in Ihrem gesamten Programm verwenden.

Weitere Informationen finden Sie unter dieser Artikel , in dem das Liskov-Substitutionsprinzip erläutert wird, insbesondere der Teil zum Open-Closed-Prinzip. Sie sollten in Ihrem Programm ein klares Verständnis dafür haben, wo Sie Objekte definieren und wo Sie sie verwenden. Wenn sie sich nicht am selben Ort befinden, sollten Sie auf jeden Fall Schnittstellen verwenden, um zu definieren, wie sie verwendet werden .

3
Neil

Grundsätzlich sollten Sie die kleinste Menge an Informationen verwenden, die erforderlich ist.

Wenn in Ihrem Beispiel Orange die Schnittstelle Fruit implementiert und Ihr Code ohne das Wissen, dass das Objekt ein Orange-Objekt ist, einwandfrei funktioniert, sollte es als Fruit-Schnittstelle deklariert werden. Dies bedeutet, dass Sie nichts ändern müssen, wenn Sie ein Erdbeerobjekt haben möchten, außer die erste Zeile in "Fruit obj = new Strawberry ();" zu ändern. Wenn Teile des folgenden Codes ein Orange-Objekt erwarten und Methoden verwenden, die in der Fruit-Oberfläche nicht vorhanden sind, beginnen Sie natürlich mit "Orange obj = new Orange ();" Und Sie wissen, dass Ihr Code nicht nur mit einem Erdbeerobjekt funktioniert. Das ist schwierig, es erfordert Codeänderungen, aber diese Codeänderungen sind notwendig.

Wenn Sie die von Orange implementierte Schnittstelle ändern, können Sie diese Schnittstelle nicht einfach ändern. Sie implementieren beide Schnittstellen. Oder Sie entfernen die für Fruit erforderlichen Methoden und fügen Methoden für die neue Benutzeroberfläche hinzu. Ihr Code, der das Orange-Objekt erstellt, funktioniert nicht mehr. Orange deklarieren obj = new Orange (); hätte dir nicht geholfen, weil du höchstwahrscheinlich Schnittstellenmethoden aufgerufen hast, die nicht mehr da sind. Das Ändern der Schnittstelle ist eine Haupt- Änderung mit Hauptfolgen. Es ist unwahrscheinlich, dass Ihre FruitSalad-Klasse mit einem Orange-Objekt arbeiten kann, wenn sie die Fruit-Schnittstelle nicht implementiert.

2
gnasher729

Dies kommt auf die Frage von

Warum abstrahieren wir überhaupt?

Der Punkt der Abstraktion ist, mehr mit weniger zu tun. Wenn Sie Code als Regeln like sprechen Sie nicht mit Männern, von denen Sie nicht wissen, wer dunkle Anzüge und schwarze Hüte trägt Und don ' t rede mit Fremden du hast zwei Regeln, wobei die letztere abstrakter ist als die erstere.

Sie haben also eine abstrakte Regel, die weniger Wörter enthält, allgemeiner ist und sich so mit mehr Fällen befasst.

Aber was ist mit im Gespräch mit Ärzten was a) Fremde und b) Männer sein können?

Dann macht die Regel keinen Sinn und Sie müssen sich mit Ausnahmen von der Regel befassen.

Was hat das mit Ihrem Problem zu tun?

Mit einer Schnittstelle von Fruit haben Sie eine Abstraktion erstellt. Und jede Anweisung, die sich mit $things, die sich wie fruits verhalten, ist sehr allgemein und kann auf viele fruchtig Dinge angewendet werden.

Sie möchten nicht für jedes einzelne bekannte Fruit Code schreiben.

Aber wie im obigen Beispiel fällt dies auseinander, wenn Sie Orange Verhalten benötigen, das reicher sein kann als nur fruchtiges Verhalten.

"In eine Schnittstelle implementieren" ist also eine Faustregel Was bedeutet:

Es ist eine gute Idee, Objekte, die ein gemeinsames Verhalten aufweisen, zusammen mit einer Schnittstelle zu gruppieren und Anweisungen für diese Objekte zu schreiben, um weniger Code als für jedes einzelne Objekt zu schreiben. Aber manchmal gilt diese Regel nicht.

P.: "Kopplung" bedeutet hier "je nach Spezialisierung" oder weniger allgemein. Wenn Sie also allgemein Regeln erstellen, vermeiden Sie Spezialisierung = Kopplung.

0
Thomas Junk