it-swarm.com.de

Wann sollte ich eine Java Swing-Klasse) erweitern?

Mein derzeitiges Verständnis der Vererbungsimplementierung ist, dass man eine Klasse nur erweitern sollte, wenn eine IS-A Beziehung vorhanden ist. Wenn die übergeordnete Klasse weiterhin spezifischere untergeordnete Typen mit unterschiedlichen Funktionen haben kann, jedoch gemeinsame Elemente verwendet, die im übergeordneten Element abstrahiert sind.

Ich stelle dieses Verständnis in Frage, weil mein Java Professor uns dies empfiehlt. Er hat empfohlen, dass wir für eine JSwing Anwendung im Unterricht bauen

Man sollte alle JSwing Klassen (JFrame, JButton, JTextBox usw.) in separate benutzerdefinierte Klassen erweitern und darin eine GUI-bezogene Anpassung angeben (wie die Komponente) Größe, Komponentenetikett usw.)

So weit so gut, aber er rät weiter, dass jeder JButton seine eigene erweiterte Klasse haben sollte, obwohl der einzige Unterscheidungsfaktor das Label ist.

Zum Beispiel Wenn die GUI zwei Schaltflächen hat Okay und Abbrechen. Er empfiehlt, sie wie folgt zu verlängern:

class OkayButton extends JButton{
    MainUI mui;
    public OkayButton(MainUI mui) {
        setSize(80,60);
        setText("Okay");
        this.mui = mui;
        mui.add(this);        
    }
}

class CancelButton extends JButton{
    MainUI mui;
    public CancelButton(MainUI mui) {
        setSize(80,60);
        setText("Cancel");
        this.mui = mui;
        mui.add(this);        
    }
}

Wie Sie sehen können, besteht der einzige Unterschied in der Funktion setText.

Ist das also Standard?

Übrigens heißt der Kurs, in dem dies besprochen wurde, Best Programming Practices in Java

[Antwort vom Prof]

Also habe ich das Problem mit dem Professor besprochen und alle in den Antworten genannten Punkte angesprochen.

Seine Rechtfertigung ist, dass Unterklassen wiederverwendbaren Code liefern, während sie den GUI-Designstandards folgen. Wenn der Entwickler beispielsweise benutzerdefinierte Schaltflächen Okay und Cancel in einem Fenster verwendet hat, ist es einfacher, dieselben Schaltflächen auch in anderen Fenstern zu platzieren.

Ich verstehe den Grund, den ich nehme, aber es nutzt immer noch nur die Vererbung und macht Code zerbrechlich.

Später könnte jeder Entwickler versehentlich das setText auf einer Okay -Taste aufrufen und ändern. Die Unterklasse wird in diesem Fall nur störend.

37
Paras

Dies verstößt gegen das Liskov-Substitutionsprinzip , da ein OkayButton an keiner Stelle ersetzt werden kann, an der ein Button erwartet wird. Sie können beispielsweise die Beschriftung einer beliebigen Schaltfläche nach Belieben ändern. Wenn Sie dies jedoch mit einem OkayButton tun, werden die internen Invarianten verletzt.

Dies ist ein klassischer Missbrauch der Vererbung für die Wiederverwendung von Code. Verwenden Sie stattdessen eine Hilfsmethode.

Ein weiterer Grund, dies nicht zu tun, ist, dass dies nur ein verschlungener Weg ist, um dasselbe zu erreichen, was linearer Code tun würde.

22
usr

Es ist in jeder Hinsicht schrecklich. Verwenden Sie bei most eine Factory-Funktion, um JButtons zu erzeugen. Sie sollten nur von ihnen erben, wenn Sie ernsthafte Erweiterungsbedürfnisse haben.

50
DeadMG

Dies ist eine sehr ungewöhnliche Art, Swing-Code zu schreiben. Normalerweise erstellen Sie nur selten Unterklassen von Swing-UI-Komponenten, am häufigsten JFrame (um untergeordnete Fenster und Ereignishandler einzurichten), aber selbst diese Unterklassen sind unnötig und werden von vielen nicht empfohlen. Das Anpassen von Text an Schaltflächen usw. erfolgt normalerweise durch Erweitern der AbstractAction-Klasse (oder der Aktionsschnittstelle , die sie bereitstellt). Dies kann Text, Symbole und andere notwendige visuelle Anpassungen bereitstellen und diese mit dem tatsächlichen Code verknüpfen, den sie darstellen. Dies ist eine weitaus bessere Methode zum Schreiben von UI-Code als die von Ihnen gezeigten Beispiele.

(Übrigens Google Scholar hat noch nie von dem von Ihnen zitierten Artikel gehört - Haben Sie eine genauere Referenz?)

12
Jules

IMHO, Best Programming Practices in Java werden in Joshua Blochs Buch "Effective Java" definiert. Es ist großartig, dass Ihr Lehrer Ihnen OOP) Übungen gibt, und es ist wichtig um zu lernen, wie man die Programmierstile anderer Leute liest und schreibt. Aber außerhalb von Josh Blochs Buch gehen die Meinungen über Best Practices ziemlich stark auseinander.

Wenn Sie diese Klasse erweitern möchten, können Sie auch die Vererbung nutzen. Erstellen Sie eine MyButton-Klasse, um den allgemeinen Code zu verwalten, und unterteilen Sie ihn für die variablen Teile:

class MyButton extends JButton{
    protected final MainUI mui;
    public MyButton(MainUI mui, String text) {
        setSize(80,60);
        setText(text);
        this.mui = mui;
        mui.add(this);        
    }
}

class OkayButton extends MyButton{
    public OkayButton(MainUI mui) {
        super(mui, "Okay");
    }
}

class CancelButton extends MyButton{
    public CancelButton(MainUI mui) {
        super(mui, "Cancel");
    }
}

Wann ist das eine gute Idee? Wenn Sie die von Ihnen erstellten Typen verwenden! Wenn Sie beispielsweise eine Funktion zum Erstellen eines Popup-Fensters haben und die Signatur lautet:

public void showPopUp(String text, JButton ok, JButton cancel)

Diese Typen, die Sie gerade erstellt haben, sind überhaupt nicht gut. Aber:

public void showPopUp(String text, OkButton ok, CancelButton cancel)

Jetzt haben Sie etwas Nützliches erstellt.

  1. Der Compiler überprüft, ob showPopUp einen OkButton und einen CancelButton verwendet. Jemand , der den Code liest , weiß, wie diese Funktion verwendet werden soll, da diese Art von Dokumentation einen Fehler bei der Kompilierung verursacht, wenn sie veraltet ist. Dies ist ein großer Vorteil. Die 1 oder 2 empirischen Studien zu den Vorteilen der Typensicherheit ergaben, dass das Verständnis des menschlichen Codes der einzige quantifizierbare Vorteil war.

  2. Dies verhindert auch Fehler, bei denen Sie die Reihenfolge der Schaltflächen umkehren, die Sie an die Funktion übergeben. Diese Fehler sind schwer zu erkennen, aber sie sind auch ziemlich selten, so dass dies ein kleiner Vorteil ist. Dies wurde verwendet, um Typensicherheit zu verkaufen, hat sich jedoch empirisch nicht als besonders nützlich erwiesen.

  3. Die erste Form der Funktion ist flexibler, da zwei beliebige Tasten angezeigt werden. Manchmal ist das besser, als die Art der Schaltflächen einzuschränken. Denken Sie daran, dass Sie die Funktion für beliebige Tasten unter weiteren Umständen testen müssen. Wenn sie nur funktioniert, wenn Sie genau die richtige Art von Tasten übergeben, tun Sie niemandem einen Gefallen, indem Sie so tun, als würde sie irgendeine Art von Taste benötigen.

Das Problem bei der objektorientierten Programmierung besteht darin, dass der Wagen vor das Pferd gestellt wird. Sie müssen zuerst die Funktion schreiben, um zu wissen, was die Signatur ist, um zu wissen, ob es sich lohnt, Typen dafür zu erstellen. In Java müssen Sie jedoch zuerst Ihre Typen festlegen, damit Sie die Funktionssignatur schreiben können.

Aus diesem Grund möchten Sie vielleicht einen Clojure-Code schreiben, um zu sehen, wie großartig es sein kann, zuerst Ihre Funktionen zu schreiben. Sie können schnell codieren und dabei so viel wie möglich über die asymptotische Komplexität nachdenken. Clojures Annahme der Unveränderlichkeit verhindert Fehler genauso wie Typen in Java.

Ich bin immer noch ein Fan von statischen Typen für große Projekte und verwende jetzt einige funktionale Dienstprogramme, mit denen ich zuerst Funktionen schreiben und meine Typen später in Java benennen . Nur ein Gedanke.

P.S. Ich habe den Zeiger mui endgültig gemacht - er muss nicht veränderbar sein.

10
GlenPeterson

Ich würde wetten, dass Ihr Lehrer nicht wirklich glaubt, dass Sie eine Swing-Komponente immer erweitern sollten, um sie zu verwenden. Ich wette, sie verwenden dies nur als Beispiel, um Sie zu zwingen, das Erweitern von Klassen zu üben. Ich würde mir noch nicht allzu viele Sorgen um Best Practices in der Praxis machen.

Davon abgesehen, in der realen Welt bevorzugen wir Zusammensetzung gegenüber Vererbung .

Ihre Regel "Man sollte eine Klasse nur erweitern, wenn eine IS-A-Beziehung vorliegt" ist unvollständig. Es sollte mit "... enden und wir müssen das Standardverhalten der Klasse ändern" in großen fetten Buchstaben.

Ihre Beispiele erfüllen diese Kriterien nicht. Sie müssen die Klasse extend nicht JButton, nur um ihren Text festzulegen. Sie müssen die Klasse extend nicht JFrame, nur um Komponenten hinzuzufügen. Sie können diese Dinge mit ihren Standardimplementierungen problemlos ausführen. Das Hinzufügen von Vererbung führt also nur zu unnötigen Komplikationen.

Wenn ich eine Klasse sehe, die extends eine andere Klasse ist, werde ich mich fragen, was diese Klasse ist ändert sich. Wenn Sie nichts ändern, lassen Sie mich überhaupt nicht durch die Klasse schauen.

Zurück zu Ihrer Frage: wann sollten Sie eine Java Klasse ? erweitern, wenn Sie einen wirklich, wirklich, wirklich guten Grund haben, eine Klasse zu erweitern.

Hier ein spezielles Beispiel: Eine Möglichkeit zum benutzerdefinierten Malen (für ein Spiel oder eine Animation oder nur für eine benutzerdefinierte Komponente) besteht darin, die Klasse JPanel zu erweitern. (Weitere Informationen dazu hier .) Sie erweitern die Klasse JPanel, weil Sie überschreiben müssen die Funktion paintComponent(). Sie sind tatsächlich ändern das Verhalten der Klasse, indem Sie dies tun. Sie können mit der Standardimplementierung JPanel kein benutzerdefiniertes Malen ausführen.

Aber wie gesagt, Ihr Lehrer benutzt diese Beispiele wahrscheinlich nur als Ausrede, um Sie zu zwingen, das Erweitern des Unterrichts zu üben.

8
Kevin Workman