Ich habe eine Methode, die 30 Parameter akzeptiert. Ich nahm die Parameter und legte sie in eine Klasse, so dass ich nur einen Parameter (die Klasse) an die Methode übergeben konnte. Ist es im Falle eines Refactorings vollkommen in Ordnung, ein Objekt zu übergeben, das alle Parameter kapselt, auch wenn dies alles ist, was es enthält?
c#
oop
refactoring
Xaisoft
quelle
quelle
Antworten:
Das ist eine großartige Idee. In der Regel werden Datenverträge beispielsweise in WCF ausgeführt.
Ein Vorteil dieses Modells besteht darin, dass der Verbraucher der Klasse beim Hinzufügen eines neuen Parameters nicht nur Änderungen vornehmen muss, um den Parameter hinzuzufügen.
Wie David Heffernan erwähnt, kann es helfen, den Code selbst zu dokumentieren:
FrobRequest frobRequest = new FrobRequest { FrobTarget = "Joe", Url = new Uri("http://example.com"), Count = 42, }; FrobResult frobResult = Frob(frobRequest);
quelle
Während andere Antworten hier richtig darauf hinweisen, dass das Übergeben einer Instanz einer Klasse besser ist als das Übergeben von 30 Parametern, sollten Sie sich bewusst sein, dass eine große Anzahl von Parametern ein Symptom für ein zugrunde liegendes Problem sein kann.
Beispielsweise nehmen statische Methoden häufig an Anzahl von Parametern zu, da sie die ganze Zeit über Instanzmethoden gewesen sein sollten, und Sie übergeben viele Informationen, die in einer Instanz dieser Klasse einfacher verwaltet werden könnten.
Alternativ können Sie nach Möglichkeiten suchen, die Parameter in Objekte einer höheren Abstraktionsebene zu gruppieren. Das Ablegen einer Reihe nicht verwandter Parameter in eine einzelne Klasse ist ein letzter Ausweg, IMO.
Siehe Wie viele Parameter sind zu viele? für einige weitere Ideen dazu.
quelle
Es ist ein guter Anfang. Aber jetzt, wo Sie diese neue Klasse haben, sollten Sie Ihren Code auf den Kopf stellen. Verschieben Sie die Methode, die diese Klasse als Parameter verwendet, in Ihre neue Klasse (übergeben Sie natürlich eine Instanz der ursprünglichen Klasse als Parameter). Jetzt haben Sie eine große Methode, allein in einer Klasse, und es wird einfacher sein, sie in kleinere, besser handhabbare und testbare Methoden zu zerlegen. Einige dieser Methoden werden möglicherweise wieder in die ursprüngliche Klasse verschoben, aber ein angemessener Teil wird wahrscheinlich in Ihrer neuen Klasse verbleiben. Sie sind über " Parameterobjekt einführen" hinausgegangen, um die Methode durch das Methodenobjekt zu ersetzen .
Eine Methode mit dreißig Parametern ist ein ziemlich starkes Zeichen dafür, dass die Methode zu lang und zu kompliziert ist. Zu schwer zu debuggen, zu schwer zu testen. Sie sollten also etwas dagegen tun, und "Parameterobjekt einführen" ist ein guter Ausgangspunkt.
quelle
Obwohl das Refactoring eines Parameterobjekts an sich keine schlechte Idee ist, sollte es nicht verwendet werden, um das Problem zu verbergen, dass eine Klasse, die 30 von einer anderen Stelle bereitgestellte Daten benötigt, immer noch nach Code riecht. Das Refactoring von Introduct Parameter Object sollte wahrscheinlich als ein Schritt auf dem Weg zu einem umfassenderen Refactoring-Prozess angesehen werden und nicht als das Ende dieses Verfahrens.
Eines der Probleme, auf das es nicht wirklich eingeht, ist das von Feature Envy. Bedeutet die Tatsache, dass die Klasse, an die das Parameterobjekt übergeben wird, so sehr an den Daten einer anderen Klasse interessiert ist, nicht, dass die Methoden, die mit diesen Daten arbeiten, möglicherweise an den Ort verschoben werden sollten, an dem sich die Daten befinden? Es ist wirklich besser, Cluster von Methoden und Daten zu identifizieren, die zusammengehören, und sie in Klassen zu gruppieren, wodurch die Kapselung erhöht und Ihr Code flexibler wird.
Nach mehreren Iterationen der Aufteilung des Verhaltens und der Daten, mit denen es arbeitet, in separate Einheiten sollten Sie feststellen, dass Sie keine Klassen mit einer enormen Anzahl von Abhängigkeiten mehr haben. Dies ist immer ein besseres Endergebnis, da Ihr Code dadurch geschmeidiger wird.
quelle
Das ist eine ausgezeichnete Idee und eine sehr verbreitete Lösung für das Problem. Methoden mit mehr als 2 oder 3 Parametern werden exponentiell immer schwieriger zu verstehen.
Wenn Sie all dies in einer einzigen Klasse zusammenfassen, wird der Code viel klarer. Da Ihre Eigenschaften Namen haben, können Sie selbstdokumentierenden Code wie folgt schreiben:
params.height = 42; params.width = 666; obj.doSomething(params);
Wenn Sie viele Parameter haben, ist die Alternative, die auf der Positionsidentifikation basiert, natürlich einfach schrecklich.
Ein weiterer Vorteil besteht darin, dass dem Schnittstellenvertrag zusätzliche Parameter hinzugefügt werden können, ohne dass an allen Anrufstellen Änderungen erzwungen werden müssen. Dies ist jedoch nicht immer so trivial, wie es scheint. Wenn unterschiedliche Aufrufstellen unterschiedliche Werte für den neuen Parameter erfordern, ist es schwieriger, sie zu ermitteln als mit dem parameterbasierten Ansatz. Beim parameterbasierten Ansatz erzwingt das Hinzufügen eines neuen Parameters an jeder Aufrufstelle eine Änderung, um den neuen Parameter bereitzustellen, und Sie können den Compiler die Aufgabe übernehmen, alle zu finden.
quelle
Martin Fowler nennt dieses Introduce Parameter Object in seinem Buch Refactoring . Mit diesem Zitat würden es nur wenige eine schlechte Idee nennen.
quelle
30 Parameter ist ein Chaos. Ich finde es viel schöner, eine Klasse mit den Eigenschaften zu haben. Sie können sogar mehrere "Parameterklassen" für Gruppen von Parametern erstellen, die in dieselbe Kategorie passen.
quelle
Sie können auch eine Struktur anstelle einer Klasse verwenden.
Aber was Sie versuchen zu tun, ist sehr verbreitet und eine großartige Idee!
quelle
Es kann sinnvoll sein, eine Plain Old Data- Klasse zu verwenden, unabhängig davon, ob Sie ein Refactoring durchführen oder nicht. Ich bin gespannt, warum Sie das nicht gedacht haben.
quelle
Vielleicht sind die optionalen und benannten Parameter von C # 4.0 eine gute Alternative dazu?
Auf jeden Fall kann die von Ihnen beschriebene Methode auch zur Abstraktion des Programmverhaltens geeignet sein. Zum Beispiel könnten Sie eine Standardfunktion
SaveImage(ImageSaveParameters saveParams)
in einer Schnittstelle haben, dieImageSaveParameters
auch eine Schnittstelle ist, und können abhängig vom Bildformat zusätzliche Parameter haben. Zum BeispielJpegSaveParameters
hat eineQuality
-Eigenschaft, währendPngSaveParameters
eineBitDepth
-Eigenschaft hat.So funktioniert das Speichern-Speichern-Dialogfeld in Paint.NET, so dass es ein sehr reales Beispiel ist.
quelle
Wie bereits erwähnt: Dies ist der richtige Schritt, aber beachten Sie auch Folgendes:
quelle
So viele tolle Antworten hier. Ich möchte meine zwei Cent hinzufügen.
Parameterobjekt ist ein guter Anfang. Aber es könnte noch mehr getan werden. Betrachten Sie Folgendes (Rubinbeispiele):
/ 1 / Anstatt einfach alle Parameter zu gruppieren, prüfen Sie, ob eine sinnvolle Gruppierung von Parametern möglich ist. Möglicherweise benötigen Sie mehr als ein Parameterobjekt.
def display_line(startPoint, endPoint, option1, option2)
könnte werden
def display_line(line, display_options)
/ 2 / Das Parameterobjekt hat möglicherweise eine geringere Anzahl von Eigenschaften als die ursprüngliche Anzahl von Parametern.
könnte werden
def double_click?(first_click_info, second_click_info) # MouseClickInfo being the parameter object type # having cursor_location and control_at_click as properties
Mithilfe solcher Verwendungen können Sie Möglichkeiten entdecken, diesen Parameterobjekten ein aussagekräftiges Verhalten hinzuzufügen. Sie werden feststellen, dass sie ihren anfänglichen Datenklassengeruch früher zu Ihrem Komfort abschütteln . : -)
quelle