Ist die Kopplung mit Strings „lockerer“ als mit Klassenmethoden?

18

Ich starte ein Schulgruppenprojekt in Java mit Swing. Es ist eine unkomplizierte grafische Benutzeroberfläche der Database Desktop App.

Der Professor gab uns den Code aus dem letztjährigen Projekt, damit wir sehen konnten, wie er die Dinge macht. Mein erster Eindruck ist, dass der Code weitaus komplizierter ist, als er eigentlich sein sollte, aber ich stelle mir vor, dass Programmierer dies oft denken, wenn sie sich Code ansehen, den sie nicht nur geschrieben haben.

Ich hoffe Gründe zu finden, warum sein System gut oder schlecht ist. (Ich fragte den Professor und er sagte, ich würde später sehen, warum es besser ist, was mich nicht zufriedenstellt.)

Um eine Kopplung zwischen seinen beständigen Objekten, Modellen (Geschäftslogik) und Ansichten zu vermeiden, wird grundsätzlich alles mit Zeichenfolgen ausgeführt. Die persistenten Objekte, die in der Datenbank gespeichert werden, sind Hashtabellen von Zeichenfolgen, und die Modelle und Ansichten "abonnieren" sich gegenseitig und stellen Zeichenfolgenschlüssel für die "Ereignisse" bereit, die sie abonnieren.

Wenn ein Ereignis ausgelöst wird, sendet die Ansicht oder das Modell eine Zeichenfolge an alle Abonnenten, die entscheiden, was für dieses Ereignis zu tun ist. Zum Beispiel in einer der Listener-Methoden für Ansichtenaktionen (ich glaube, dies setzt das bicycleMakeField nur auf das persistable-Objekt):

    else if(evt.getSource() == bicycleMakeField)
    {
        myRegistry.updateSubscribers("BicycleMake", bicycleMakeField.getText());
    }

Dieser Aufruf ruft schließlich diese Methode im Fahrzeugmodell auf:

public void stateChangeRequest(String key, Object value) {

            ... bunch of else ifs ...

    else
    if (key.equals("BicycleMake") == true)
    {
                ... do stuff ...

Der Professor sagt, dass diese Art, Dinge zu tun, erweiterbarer und wartbarer ist, als wenn die Ansicht einfach eine Methode für ein Geschäftslogikobjekt aufruft. Er sagt, dass es keine Kopplung zwischen den Ansichten und Modellen gibt, weil sie nicht von der Existenz der anderen wissen.

Ich denke, dies ist eine schlechtere Art der Kopplung, da Ansicht und Modell die gleichen Zeichenfolgen verwenden müssen, um zu funktionieren. Wenn Sie eine Ansicht oder ein Modell entfernen oder einen Tippfehler in einer Zeichenfolge machen, werden keine Kompilierungsfehler angezeigt. Es macht den Code auch viel länger, als ich denke, dass es sein muss.

Ich möchte dies mit ihm besprechen, aber er nutzt seine Branchenerfahrung, um jedem Argument entgegenzuwirken, das ich als unerfahrener Student vorbringe. Hat sein Ansatz einen Vorteil, den ich vermisse?


Um dies zu verdeutlichen, möchte ich den obigen Ansatz mit der Tatsache vergleichen, dass die Sichtweise offensichtlich an das Modell gekoppelt ist. Sie können beispielsweise das Fahrzeugmodellobjekt an die Ansicht übergeben und anschließend die "Marke" des Fahrzeugs ändern:

vehicle.make = bicycleMakeField.getText();

Dies würde 15 Codezeilen, die derzeit NUR verwendet werden, um die Marke des Fahrzeugs an einer Stelle festzulegen, auf eine einzige lesbare Codezeile reduzieren. (Und da diese Art von Operation hunderte Male in der gesamten App ausgeführt wird, wäre dies meiner Meinung nach ein großer Gewinn für die Lesbarkeit und Sicherheit.)


Aktualisieren

Mein Teamleiter und ich haben das Framework mithilfe der statischen Typisierung so umstrukturiert, wie wir es wollten, den Professor informiert und ihm schließlich eine Demo gegeben. Er ist so großzügig, dass wir unser Framework nutzen können, solange wir ihn nicht um Hilfe bitten und wenn wir den Rest unseres Teams auf dem Laufenden halten können - was uns fair erscheint.

Philip
quelle
12
OT. Ich denke, Ihr Professor sollte sich selbst aktualisieren und keinen alten Code verwenden. In der Wissenschaft gibt es keinen Grund, eine JAVA-Version von 2006 zu verwenden, wenn es 2012 ist.
Farmor
3
Bitte halten Sie uns auf dem Laufenden, wenn Sie irgendwann seine Antwort erhalten.
JeffO 31.01.12
4
Unabhängig von der Qualität des Codes oder der Weisheit der Technik sollten Sie das Wort "Magie" aus Ihrer Beschreibung der als Bezeichner verwendeten Zeichenfolgen streichen. "Magic Strings" impliziert, dass das System nicht logisch ist und Ihre Sichtweise beeinträchtigt und jede Diskussion, die Sie mit Ihrem Kursleiter führen, vergiftet. Wörter wie "Schlüssel", "Namen" oder "Bezeichner" sind neutraler, vielleicht aussagekräftiger und führen mit größerer Wahrscheinlichkeit zu einer hilfreichen Konversation.
Caleb
1
Wahr. Ich habe sie keine magischen Saiten genannt, als ich mit ihm gesprochen habe, aber Sie haben Recht, es muss nicht schlimmer klingen als es ist.
Philip
2
Der Ansatz, den Ihr Professor beschreibt (Benachrichtigung der Zuhörer über Ereignisse, die als Zeichenfolgen bezeichnet werden), kann gut sein. Eigentlich könnten Aufzählungen sinnvoller sein, aber das ist nicht das größte Problem. Das eigentliche Problem, das ich hier sehe, ist, dass das Objekt, das die Benachrichtigung erhält, das Modell selbst ist, und dass Sie dann basierend auf der Art des Ereignisses manuell versenden müssen. In einer Sprache mit erstklassigen Funktionen würden Sie eine Funktion und kein Objekt für ein Ereignis registrieren . In Java sollte man eine Art Objekt verwenden, das eine einzelne Funktion
Andrea

Antworten:

32

Der Ansatz, den Ihr Professor vorschlägt, lässt sich am besten als strikt getippt beschreiben und ist auf fast jeder Ebene falsch.

Die Kopplung lässt sich am besten durch Abhängigkeitsinversion reduzieren , die durch eine polymorphe Verteilung erfolgt, anstatt eine Reihe von Fällen fest zu verdrahten.

back2dos
quelle
3
Sie schreiben "fast jedes Level", aber Sie haben nicht geschrieben, warum es in diesem Beispiel (Ihrer Meinung nach) kein passender Fall ist. Wäre interessant zu wissen.
Hakre
1
@hakre: Es gibt keine geeigneten Fälle, zumindest nicht in Java. Ich sagte, es ist "auf fast jeder Ebene falsch", weil es besser ist, als überhaupt keine Indirektion zu verwenden und den gesamten Code an einer Stelle zu werfen. Die Verwendung von (globalen) Konstanten für magische Zeichenfolgen als Grundlage für einen manuellen Versand ist jedoch nur eine verschlungene Methode, um globale Objekte letztendlich zu verwenden.
back2dos
Ihr Argument lautet also: Es gibt nie passende Fälle, also ist dies auch keiner? Das ist kaum ein Argument und eigentlich nicht sehr hilfreich.
Hakre
1
@hakre: Mein Argument ist, dass dieser Ansatz aus einer Reihe von Gründen schlecht ist (Absatz 1 - genug Gründe, dies zu vermeiden, sind in der Erklärung von stringent typisiert) und sollten nicht verwendet werden, da es einen besseren gibt (Absatz 2) ).
back2dos
3
@hakre Ich kann mir eine Situation vorstellen, die diesen Ansatz rechtfertigen würde, wenn ein Serienmörder Sie an Ihren Stuhl geklebt und eine Kettensäge über Ihren Kopf gehalten hätte. Abgesehen davon ist es vorzuziehen, sich auf Sprachfunktionen zu verlassen, um diese Art von Dingen auszuführen. Eigentlich kann ich mir einen anderen Fall vorstellen, bei dem es um Krokodile und Ninjas geht, aber das ist etwas komplizierter.
Rob
19

Ihr Professor macht es falsch . Er versucht, die Ansicht und das Modell vollständig zu entkoppeln, indem er die Kommunikation dazu zwingt, über eine Zeichenkette in beide Richtungen zu wandern (Ansicht hängt nicht vom Modell ab und Modell hängt nicht von der Ansicht ab) , was den Zweck von objektorientiertem Design und MVC völlig zunichte macht. Anstatt Klassen zu schreiben und eine OO-basierte Architektur zu entwerfen, schreibt er unterschiedliche Module , die einfach auf textbasierte Nachrichten reagieren.

Um dem Professor gegenüber fair zu sein, versucht er, in Java eine Oberfläche zu erstellen, die der häufig verwendeten .NET-Schnittstelle sehr ähnlich ist INotifyPropertyChanged, die Abonnenten über Änderungsereignisse informiert, indem Zeichenfolgen übergeben werden, die angeben, welche Eigenschaft geändert wurde. Zumindest in .NET wird diese Schnittstelle hauptsächlich für die Kommunikation von einem Datenmodell zum Anzeigen von Objekten und GUI-Elementen verwendet (Bindung).

Wenn es richtig gemacht , diesen Ansatz können einige benefits¹ haben:

  1. Die Ansicht hängt vom Modell ab, das Modell muss jedoch nicht von der Ansicht abhängen. Dies ist eine angemessene Umkehrung der Kontrolle .
  2. In Ihrem View-Code gibt es nur eine Stelle, an der Sie auf Änderungsbenachrichtigungen des Modells reagieren können, und nur eine Stelle, an der Sie sich anmelden bzw. abmelden können. Auf diese Weise wird die Wahrscheinlichkeit eines hängenden Speicherverlusts verringert.
  3. Der Codierungsaufwand für das Hinzufügen oder Entfernen eines Ereignisses zum Ereignisherausgeber ist erheblich geringer. In .NET gibt es einen eingebauten Publish / Subscribe-Mechanismus, der aufgerufen wird. eventsIn Java müssten Sie jedoch Klassen oder Objekte erstellen und für jedes Ereignis einen Code zum Abonnieren / Abbestellen schreiben.
  4. Der größte Nachteil bei diesem Ansatz besteht darin, dass der Abonnementcode nicht mehr mit dem Veröffentlichungscode synchronisiert werden kann. Dies ist jedoch auch in einigen Entwicklungsumgebungen von Vorteil. Wenn im Modell ein neues Ereignis entwickelt wird und die Ansicht noch nicht aktualisiert wurde, um es zu abonnieren, funktioniert die Ansicht wahrscheinlich weiterhin. Wenn ein Ereignis entfernt wird, ist zwar Code nicht mehr verfügbar, er kann jedoch kompiliert und ausgeführt werden.
  5. Zumindest in .NET wird Reflection hier sehr effektiv verwendet, um abhängig von der Zeichenfolge, die an den Abonnenten übergeben wurde, automatisch Get- und Setter aufzurufen.

Kurz gesagt (und um Ihre Frage zu beantworten), es ist möglich, aber der Ansatz Ihres Professors ist schuld daran, dass er nicht mindestens eine einseitige Abhängigkeit zwischen Ansicht und Modell zulässt. Es ist einfach nicht praktisch, zu akademisch und kein gutes Beispiel für die Verwendung der vorhandenen Tools.


¹ Durchsuchen Sie Google INotifyPropertyChangednach einer Million Möglichkeiten, um zu versuchen, bei der Implementierung die Verwendung magischer Zeichenfolgen zu vermeiden .

Kevin McCormick
quelle
Es hört sich so an, als hätten Sie SOAP hier beschrieben. : D… (aber ja, ich verstehe und du hast recht)
Konrad Rudolph
11

enums (oder public static final Strings) sollten anstelle von magischen Zeichenfolgen verwendet werden.

Ihr Professor versteht OO nicht . Zum Beispiel mit einer konkreten Fahrzeugklasse?
Und damit diese Klasse die Marke eines Fahrrads versteht?

Das Fahrzeug sollte abstrakt sein und es sollte eine konkrete Unterklasse namens Bike geben. Ich würde nach seinen Regeln spielen, um eine gute Note zu bekommen und dann seinen Unterricht vergessen.

Farmor
quelle
3
Es sei denn, in dieser Übung geht es darum, den Code mithilfe besserer OO-Prinzipien zu verbessern. In diesem Fall Refactor entfernt.
Joshin4colours
2
@ joshin4colours Wenn StackExchange es bewertet hätte, hätten Sie recht. aber da der grader derselbe ist, der auf die idee gekommen ist, würde er uns allen schlechte noten geben, weil er weiß, dass seine umsetzung besser war.
Dan Neely
7

Ich denke, Sie haben einen guten Punkt, die magischen Saiten sind schlecht. Jemand in meinem Team musste vor ein paar Wochen ein Problem beheben, das durch magische Strings verursacht wurde (aber es war in ihrem eigenen Code, den sie geschrieben hatten, also hielt ich den Mund). Und es ist passiert ... in der Industrie !!

Der Vorteil seines Ansatzes ist, dass es möglicherweise schneller ist, Code zu erstellen, insbesondere für kleine Demo-Projekte. Nachteile sind, dass es zu Tippfehlern kommt und je öfter die Zeichenfolge verwendet wird, desto größer ist die Wahrscheinlichkeit, dass ein Tippfehler die Sache durcheinander bringt. Und wenn Sie jemals eine magische Zeichenfolge ändern möchten, ist das Umgestalten von Zeichenfolgen schwieriger als das Umgestalten von Variablen, da Umgestaltungswerkzeuge möglicherweise auch Zeichenfolgen berühren, die die magische Zeichenfolge enthalten (als Teilzeichenfolge), aber selbst nicht die magische Zeichenfolge sind, und nicht berührt werden sollten. Außerdem müssen Sie möglicherweise ein Wörterbuch oder einen Index der magischen Zeichenfolgen führen, damit neue Entwickler nicht zum selben Zweck neue magische Zeichenfolgen erfinden und keine Zeit damit verschwenden, nach vorhandenen magischen Zeichenfolgen zu suchen.

In diesem Zusammenhang sieht es so aus, als ob ein Enum besser wäre als magische Zeichenfolgen oder sogar globale Zeichenfolgenkonstanten. Außerdem bieten IDEs häufig eine Art Code-Unterstützung (automatische Vervollständigung, Umgestaltung, Suche nach allen Referenzen usw.), wenn Sie konstante Zeichenfolgen oder Aufzählungen verwenden. Eine IDE bietet nicht viel Hilfe, wenn Sie magische Zeichenfolgen verwenden.

FrustratedWithFormsDesigner
quelle
Ich stimme zu, dass Aufzählungen besser sind als Zeichenkettenliterale. Aber selbst dann ist es wirklich besser, eine "stateChangeRequest" mit einem Aufzählungsschlüssel aufzurufen, oder einfach eine Methode im Modell direkt aufzurufen? In beiden Fällen sind Modell und Ansicht an dieser einen Stelle gekoppelt.
Philip
@Philip: Nun, ich denke, um das Modell und die Ansicht an diesem Punkt zu entkoppeln, benötigen Sie möglicherweise eine Art Controller-Klasse, um dies zu tun ...
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner - Ja. Eine MVC-Architektur ist die am stärksten entkoppelte
cdeszaq
Richtig, wenn eine andere Schicht hilft, sie zu entkoppeln, würde ich nicht dagegen argumentieren. Diese Ebene könnte jedoch statisch typisiert sein und tatsächliche Methoden anstelle von Zeichenfolgen- oder Aufzählungsnachrichten verwenden, um sie zu verbinden.
Philip
6

Die Verwendung von Branchenerfahrung ist ein schlechtes Argument. Ihr Professor muss sich ein besseres Argument einfallen lassen :-).

Wie andere bereits gesagt haben, ist die Verwendung von magischen Zeichenfolgen mit Sicherheit verpönt. Die Verwendung von Aufzählungen wird als weitaus sicherer angesehen. Eine Aufzählung hat Bedeutung und Umfang , magische Zeichenfolgen nicht. Abgesehen davon wird die Art und Weise, in der Ihr Professor Bedenken entkoppelt, als eine ältere und fragilere Technik angesehen, als sie heute verwendet wird.

Ich sehe, dass @backtodos dies behandelt hat, während ich darauf geantwortet habe, und daher möchte ich meiner Meinung nach hinzufügen, dass Sie mit Inversion of Control (bei dem es sich bei Dependency Injection um ein Formular handelt, das Sie in der Industrie in Kürze durch das Spring-Framework führen werden). ..) gilt als modernere Möglichkeit, mit dieser Art der Entkopplung umzugehen.

Martijn Verburg
quelle
2
Ich bin mir vollkommen einig, dass die akademischen Anforderungen weitaus höher sein sollten als die der Branche. Die Wissenschaft == bester theoretischer Weg; Die Branche == am besten praktisch
Farmor
3
Leider tun dies einige von denen, die dieses Zeug im akademischen Bereich unterrichten, weil sie es in der Industrie nicht schaffen. Positiv zu vermerken ist, dass einige Akademiker brillant sind und nicht in einer Unternehmenszentrale versteckt werden sollten.
FrustratedWithFormsDesigner
4

Lassen Sie uns ganz klar sein; Da ist unvermeidlich eine gewisse Kopplung drin. Die Tatsache, dass es ein solches abonnierbares Thema gibt, ist ebenso wie die Art des Rests der Nachricht (z. B. „einzelne Zeichenfolge“) ein Kopplungspunkt. Vor diesem Hintergrund stellt sich dann die Frage, ob die Kopplung größer ist, wenn dies über Zeichenfolgen oder Typen erfolgt. Die Antwort darauf hängt davon ab, ob Sie über eine zeitlich oder räumlich verteilte Kopplung besorgt sind: ob die Nachrichten in einer Datei aufbewahrt werden sollen, bevor sie später eingelesen werden, oder ob sie an einen anderen Prozess gesendet werden (insbesondere wenn Das ist nicht in der gleichen Sprache geschrieben. Die Verwendung von Strings kann die Sache viel einfacher machen. Der Nachteil ist, dass es keine Typprüfung gibt. Fehler werden erst zur Laufzeit erkannt (und manchmal auch dann nicht).

Eine Strategie zur Minderung / Beeinträchtigung von Java kann darin bestehen, eine typisierte Schnittstelle zu verwenden, wobei sich diese typisierte Schnittstelle jedoch in einem separaten Paket befindet. Es sollte nur aus Java interfaces und Grundwerttypen bestehen (z. B. Aufzählungen, Ausnahmen). In diesem Paket sollten keine Schnittstellen implementiert sein. Dann implementiert jeder etwas dagegen, und wenn es erforderlich ist, die Nachrichten auf komplexe Weise zu übermitteln, kann eine bestimmte Implementierung eines Java-Delegaten bei Bedarf magische Zeichenfolgen verwenden. Es macht es auch viel einfacher, den Code in eine professionellere Umgebung wie JEE oder OSGi zu migrieren, und hilft auch bei kleinen Dingen wie dem Testen.

Donal Fellows
quelle
4

Was Ihr Professor vorschlägt, ist die Verwendung von "magischen Ketten". Während es Klassen "locker" miteinander koppelt, was einfachere Änderungen ermöglicht, handelt es sich um ein Anti-Pattern. strings sollten sehr, SEHR selten verwendet werden, um Codeanweisungen zu enthalten oder zu steuern, und wenn dies unbedingt erforderlich ist, sollte der Wert der Zeichenfolgen, die Sie zur Steuerung der Logik verwenden, zentral und ständig definiert werden, damit EINE Berechtigung für den erwarteten Wert von besteht eine bestimmte Zeichenfolge.

Der Grund dafür ist sehr einfach; Zeichenfolgen werden nicht vom Compiler auf Syntax oder Konsistenz mit anderen Werten überprüft (bestenfalls werden sie auf korrekte Form in Bezug auf Escapezeichen und andere sprachspezifische Formatierungen in der Zeichenfolge überprüft). Sie können alles in eine Zeichenfolge einfügen, was Sie wollen. Als solches können Sie einen hoffnungslos kaputten Algorithmus / Programm kompilieren lassen, und erst wenn er ausgeführt wird, tritt ein Fehler auf. Betrachten Sie den folgenden Code (C #):

private Dictionary<string, Func<string>> methods;

private void InitDictionary() //called elsewhere
{
   methods = new Dictionary<string, Func<string>> 
      {{"Method1", ()=>doSomething()},
       {"MEthod2", ()=>doSomethingElse()}} //oops, fat-fingered it
}

public string RunMethod(string methodName)
{
   //very naive, but even a check for the key would generate a runtime error, 
   //not a compile-time one.
   return methods[methodName](); 
}

...

//this is what we thought we entered back in InitDictionary for this method...
var result = RunMethod("Method2"); //error; no such key

... Der gesamte Code wird beim ersten Versuch kompiliert, aber der Fehler wird beim zweiten Versuch offensichtlich. Es gibt zahlreiche Beispiele für diese Art der Programmierung, und alle unterliegen dieser Art von Fehlern, AUCH WENN Sie Zeichenfolgen als Konstanten definieren (da Konstanten in .NET in das Manifest jeder Bibliothek geschrieben werden, in der sie verwendet werden, und das muss Dann werden alle neu kompiliert, wenn der definierte Wert geändert wird, damit sich der Wert "global" ändert. Da der Fehler zur Kompilierungszeit nicht abgefangen wird, muss er während der Laufzeitprüfung abgefangen werden. Dies ist nur bei einer Codeabdeckung von 100% in Komponententests mit ausreichender Code-Übung gewährleistet, die normalerweise nur bei der absolutesten Ausfallsicherheit auftritt Echtzeit-Systeme.

KeithS
quelle
2

Tatsächlich sind diese Klassen immer noch eng miteinander verbunden. Außer jetzt sind sie so eng miteinander verbunden, dass der Compiler Ihnen nicht sagen kann, wann die Dinge kaputt sind!

Wenn jemand "BicycleMake" in "BicyclesMake" ändert, wird niemand feststellen, dass die Dinge bis zur Laufzeit kaputt sind.

Laufzeitfehler sind weitaus teurer zu beheben als Fehler bei der Kompilierung - dies ist einfach alles Mögliche.

17 von 26
quelle