Refactoring einer vorhandenen abstrakten Klasse und ihrer Parameter


8

Ich habe eine, abstract class Adie eine abstrakte Methode deklariert doStuff. Derzeit gibt es viele Klassen, die von erben Aund diese implementieren doStuff.

Die Instanzen der Klasse werden zur Laufzeit über AFactorybasierend auf Benutzereingaben initialisiert . Ursprünglich hatten alle Klassen den gleichen Parameter (die Benutzereingabe). Aber jetzt habe ich einen zusätzlichen Parameter, den nur eine neue Klasse, die erbt, Abenötigt.

Also habe ich mir die folgende Logik überlegt:

  • Die Interpreter-Klasse, die Instanzen basierend auf Benutzereingaben generiert ( AFactorynatürlich unter Verwendung), kannte diesen zusätzlichen Parameter nicht.

    • Der Versuch, es in die Klasse der Klassendolmetscher zu schieben, wäre wirklich umständlich, denn dann müsste ich wissen, wann ich es an die Fabrik weitergeben muss, was den ganzen Zweck, überhaupt eine Fabrik zu haben, zunichte macht.
    • Es blind in die Fabrik zu schicken, in der Hoffnung, dass es etwas damit anfangen könnte , scheint ebenfalls ziemlich hässlich.
  • Meine aktuelle Lösung: Inzwischen habe ich refactor beschlossen A.doStuff(Param param)in A.doStuff(AParams params).

    AParamskann alle benötigten Parameter enthalten und doStuffkann sie dann ignorieren, wenn sie nicht an ihnen interessiert sind. Dies scheint mir auch etwas umständlich zu sein und verhindert, dass ich Strukturen in WIN32API sende, die viele hässliche nutzlose Parameter enthalten können, und ich mag es nicht.

Gibt es eine elegantere Möglichkeit, dieses Problem anzugehen? Oder ein Designmuster, das ich übersehen und gelöst habe?

Anmerkungen :

  • Wir verwenden Java 1.7
  • Die Namen der Klassen sind albern, um das theoretische Designproblem hervorzuheben. Sie haben in der Realität normale indikative, bedeutungsvolle Namen :)
  • Ich habe die Suche ziemlich viel , aber habe herausgefunden, dass es ziemlich schwierig ist , das Web nach bestimmten abstrakten theoretischen Fragen zu suchen (im Gegensatz zu , warum Xwirft Exceptionin diesem Code) Ich habe beschlossen , auf jeden Fall zu fragen , so tut mir leid , wenn dies eine ist Duplikat.

Bearbeiten 1 :

  • Klarstellung: Ich muss der doStuffMethode ein unterklassenspezifisches Argument übergeben .

EDIT 2 :

  • Ich habe die Absicht von Kilian Foth nicht vollständig verstanden, deshalb habe ich Java-Pseudocode geschrieben, um das Problem besser zu erklären / Ihre Lösung zu verstehen. Damit:

    Dies ist ein Grundgerüst meines Problems.

    Dies ist ein Grundgerüst meiner Lösung.

    Dies ist meiner Meinung nach die Lösung von Kilian Foth, aber ich bin mir nicht sicher.


Können Sie klären, ob Ihr Problem darin besteht, dass Sie Ihrer Factory-Methode ein bestimmtes Argument für eine bestimmte Unterklasse hinzufügen müssen, oder ob Sie der doStuffMethode ein unterklassenspezifisches Argument übergeben müssen ?
Martin Wickman

@MartinWickman Die Antwort lautet: Ich muss ein unterklassenspezifisches Argument an die doStuff-Methode übergeben. Vielen Dank, dass Sie dies angesprochen haben. Ich entschuldige mich dafür, dass Sie nicht klar genug sind, aber ich denke, jetzt ist es besser :). Ich habe meine Frage bearbeitet ...
Scis

Für mich sieht es so aus, als hätten Sie in den von Ihnen verknüpften Lösungen auf etwas Ähnliches wie Builder gestoßen (was ich getan habe - ich hatte noch nie von Builder gehört, aber ich habe nur etwas codiert, das das tat, wofür ich es brauchte tun).
Amy Blankenship

Müssen Sie den Parameter wirklich an doStuff übergeben, oder ist der Parameterwert beim Erstellen des Objekts bekannt? Woher kommt der zusätzliche Parameter?
Aaron Kurtzhals

@AaronKurtzhals Wie ich in der Skelettdatei "Problem" geschrieben habe, ist derzeit der Wert des zusätzlichen Parameters dem EntryPointAufruf bekannt, mainder dann einige aufruft, Interpreterdie derzeit den zusätzlichen Parameter nicht als Parameter erhalten, ebenso wie die Factory. Der zusätzliche Parameter stammt von einer anderen Art von Benutzereingabe, die nicht von beschrieben wird UserInput(was ich leider nicht ändern kann).
Scis

Antworten:


9

Der Sinn einer Fabrik besteht darin, die Tatsache zu verbergen, dass Sie möglicherweise Objekte aus leicht unterschiedlichen Klassen erhalten. Wenn einige dieser Objekte bestimmte Daten benötigen und andere nicht ...

  • Entweder sind sie nicht ähnlich genug, um von derselben Fabrik hergestellt zu werden. Dann müssen Sie mehr als eine Fabrik haben und die Entscheidung viel früher treffen als derzeit.

  • Oder sie sind ähnlich genug, dass es dem Kunden egal sein soll, welche er bekommt. Dann können Sie nicht erwarten, dass sich der Client darum kümmert, ob er die zusätzlichen Daten sendet oder nicht. Daraus folgt, dass sie immer übergeben werden müssen , aber die Factory ignoriert sie manchmal, ohne den Client mit diesen Implementierungsdetails zu belästigen. Alles andere würde den Kunden mit der Unterscheidung belasten, die die Fabrik verbergen sollte.


Nun, sie sind ähnlich genug :) aber ich bin nicht sicher, ob ich Ihre Lösung verstehe. Können Sie sich bitte die bearbeitete Frage ansehen (ich habe versucht, ein Skelett zu erstellen, das das Problem und die verschiedenen Ansätze zeigt) und mir sagen, ob meine Interpretation korrekt ist oder nicht.
Scis

2

Ich gehe damit um, indem ich so etwas wie einen Builder verwende , der verschiedene Fabriken enthält. Der Builder erhält Hashes, die die Fabriken enthalten, um verschiedene Arten von Objekten zu erstellen. Er untersucht den Hash und ruft die richtige Factory basierend auf den Kriterien ab.

Zum Beispiel habe ich mehrere Fragetypen, und alle diese können durch eine Implementierung einer IQuestionFactory erstellt werden. Die meisten IQuestionFactory-Implementierungen sind Unterklassen von BaseQuestionFactory. Sie empfangen XML, das eine Frage darstellt, und analysieren es gemäß den Regeln der Frage, wobei einige Unterklassen von BaseQuestion zurückgegeben werden.

Einige Fragen benötigen weitere Informationen. Wenn es sich beispielsweise um eine Frage zum Ausfüllen von Formularen handelt, enthält möglicherweise ein Abschnitt des XML-Codes die Fragen, die die im Formular verwendeten Felder definieren. Die Fabriken, in denen diese Fragen gestellt werden, implementieren IEnhancedQuestionFactory, und der Builder überprüft die Factory, die er aus der QuestionFactoryRegistry abruft, und prüft, ob diese Schnittstelle implementiert ist. In diesem Fall wird das vollständige XML der Datei, die die Fragen enthielt, zusätzlich zu dem einzelnen XML übergeben, das an die Methode createQuestion gesendet wird.

Um Ihnen zu zeigen, wie flexibel dies sein kann, sind einige der IQuestionFactory-Implementierungen einfach Shells, die mehrere Fragenfabriken enthalten. Sie können das eingehende XML anzeigen und dann eine interne Factory aufrufen und alles zurückgeben, was dazu gehört. Wir verwenden dies, wenn eine XML-Datei mehrere Fragetypen enthält.

Es hört sich jedoch so an, als ob Sie eher die Art und Weise benötigen, wie wir Übungen erstellen (bei denen es sich im Wesentlichen um Controller handelt, die um eine Sammlung von Fragen gewickelt sind), bei denen der Hash (ExerciseFactoryMap) so eingerichtet ist, dass er die meiste Zeit eine Standardfactory zurückgibt, jedoch in bestimmten Fällen Wenn eine bestimmte Fabrik registriert ist, wird diese zurückgegeben. Und wir haben eine bestimmte Factory, die eine Übungsunterklasse mit einer zusätzlichen Eigenschaft erstellt und zusätzlich die Möglichkeit hat, das XML zu betrachten und die Informationen zu finden, die zum Auffüllen dieser Eigenschaft erforderlich sind.

Daher stimme ich Killian Foth zu, dass der Sinn einer Fabrik darin besteht, die Implementierungsdetails der Konstruktion des Objekts zu verbergen, aber nichts besagt, dass Sie nicht mehrere Fabriken auf kreative Weise kombinieren können (z. B. eine Fabrik) das enthält andere Fabriken, die die eigentliche Arbeit erledigen).


2
  • Der übliche Ansatz in solchen Fällen (Befehlsmuster) besteht darin, zusätzliche Parameter im Konstruktor zu übergeben. In Ihrem Fall, und fügen hinzu , dass die Mittel , otherArgumentum Bwährend der Bauphase.

  • Das Umdrehen der Logik kann einige Optionen eröffnen. Zum Beispiel doStuff nach AParams verschieben:

    for (String arg : listOfArgs) {
        AParams p = AParams.create(arg, otherArgument));
        for (A a : listOfA) {
            p.doStuff(a); // Let AParams to the lifting depending on type of A
        }
    }
  • Alternativ können Sie Ihren Stolz schlucken und mit instanceofin der Schleife überprüfen und otherArgumentmanuell einstellen, bevor Sie anrufen doStuff.

Wenn dies nicht möglich ist, haben Sie keine andere Wahl, als das zu tun, was Sie tun (das Parameterargument erweitern). Analysieren Sie dieses Zeug nicht zu stark.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.