2009-06-23 6 views
7

Ich neige dazu, kompakte Anwendungen erfolgreich zu schreiben, die viele Geschäftslogik auf eine einfache und nicht redundante Weise kapseln können. Ich neige dazu, kleine Methoden zu erstellen, aber mit der Zeit komme ich zu Methoden, die zu viele Parameter haben. Ich weiß, dass jeder Code sein Design erfordert, aber ich sehe ein Anti-Pattern in meinem Verhalten und ich bin mir nicht sicher, welches der beste Weg wäre, dagegen anzukämpfen.Wie vermeidet man viele Parameter in kritischen Methoden meiner Anwendungen?

public loanStructure CalculateSomeComplicatedInterestRate(enum clientType, 
     int totalAmout, bool useTaxes, bool doYearlySummary, bool doNotReloadClient, etc) 
    { 
     loanStructure ret = new loanStructure(); 
     if (doNotReloadClient) ... ; 
     ... 
     if (doYearlySummary) GroupResults(ret); 
     return ret; 
    } 

und innerhalb der Methode, ein Baum der Anrufe leitet die boolean-Einstellungen (doYearlySummary, doNotReloadClient, etc.), um verschiedene Geschäftsregeln, die auf die Berechnung wirkt:

Eine typische Situation wäre so etwas wie sein.

IE, tritt das Problem nicht auf der Tatsache liegen, dass alle Parameter in ein Objekt eingekapselt werden könnte (bigParameterStructure) ... Ich bin nicht zufrieden mit dem masterMethod Muster, aber machen Überlastungen wie CalculateSomeComplicatedInterestRateMonthly und CalculateSomeComplicatedInterestRateYearly wäre nur verstecken private CalculateSomeComplicatedInterestRate .... ein Problem !!

Von grobem objekt Orientiert Design helfen würde ... aber ich Ende-up immer noch diese Art von Methoden, die auf meinen Objekten irgendwo mit ...

Nun Jungen ... jede Hilfe ist willkommen.

Pablo.

+0

Die Geschäftslogik ist was es ist. Wenn es kompliziert ist, ist es kompliziert, nicht deine Schuld. Das sieht für mich gut aus, es sei denn, Sie haben auch Beispiele in nicht-geschäftlichem Logikcode? –

Antwort

4

Wenn Sie feststellen, dass Sie zwischen ein paar Sets mit mehreren Parametern wechseln, sollten Sie den Code in einer privaten komplizierten Methode speichern und einige öffentliche Methoden bereitstellen, die weniger Parameter verwenden und die privaten verwenden, die für einen Teil der Parameter entsprechende Standardwerte bereitstellen. Dies wird die öffentliche Schnittstelle bereinigen.

Dann, wenn Sie es wirklich aus Leistungsgründen nicht tun können (was nicht wahrscheinlich ist), brechen Sie die private Methode in Aufrufe an andere private Methoden, um Code lesbarer zu halten. Sie können Kommentare auf diese Weise loswerden, indem Sie selbstbeschreibende Methodennamen wählen.

Auf der anderen Seite, wenn einige der Parameter nicht WAS zu tun sind, aber wie man es macht (dh Sie können eine Enum haben, die zwischen einfachen und kontinuierlichen Compoundierung wechselt), sollten Sie Polymorphie verwenden und zwei Klassen implementieren eine gemeinsame Schnittstelle, jede auf eine andere Art, ODER diese Teilfunktion an eine Komponente der Hauptklasse delegieren, die von dieser zu dieser Implementierung geändert werden kann (a'la "Abhängigkeitsinjektion"). Der zweite Ansatz wird Ihren Code flexibler machen, da die Benutzer Ihrer Klasse in der Lage sind, ihre eigenen Komponenten bereitzustellen und das Verhalten Ihrer Hauptklasse zu ändern, ohne den Code zu berühren.

Sie sollten auch darüber nachdenken, wer Ihre Klasse verwendet. Vielleicht ist die Parameterliste in Ihrer Methode so lang, weil die Methode im Laufe der Zeit gewachsen ist, um den unterschiedlichen Bedürfnissen verschiedener Benutzer gerecht zu werden ("Benutzer" bedeutet in diesem Zusammenhang ein anderes Stück Code, sogar von Ihnen geschrieben)? Wenn ja, würde ich zumindest eine Methode für einen Zweck, eine andere für einen anderen Zweck erstellen. Sie können weiterhin gemeinsamen Code verwenden. Im Idealfall möchten Sie zwei Klassen haben, die zwei Zwecken dienen, um sie in der Zukunft zu ändern, ohne die anderen Funktionen zu unterbrechen.

2

Ich erstelle normalerweise "Filter" Strukturen, die ich an diese Methoden weitergeben kann, also in Ihrem Fall.

(quick and dirty, nicht die Produktionsqualität.)

public struct InterestRateCalculationFilter 
{ 
    public enum ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    public bool UseTaxes {get;set;} 
    public bool DoYearlySummary {get;set;} 
    public bool DoNotReloadClient {get;set;} 

    //Constructors go here, set defaults, etc. 
} 

Verbrauch:

InterestRateCalculationFilter filter = new InterestRateCalculationFilter(); 
filter.ClientType = Customer; 
filter.TotalAmount = 700; 
filter.UserTaxes = true; 
filter.DoYearlySummary = false; 

LoanStructure loanStructure = CalculateSomeComplicatedInterestRate(filter); 

Dies ist nicht immer eine gute Idee, aber ich oft genug, um es zu gehen, wenn der Parameter Listen erhalten über zehn Elemente, und sie fallen nicht notwendigerweise in eine Objektstruktur.

+1

Besser nicht lassen http://Stackoverflow.com/users/23354/marc-gravell diese Strukturen sehen, oder er wird überall über Sie wie ein Ausschlag sein ;-) fwiw obwohl, finde ich Klasse statt Struktur nützlicher (weil struct ist copy by) in diesem fall ** _ wenn _ ** der callee ändert die eingabewerte, ansonsten müssen sie ref übergeben. Betrachte auch die Objektinitialisierung (csc> = 3 denke ich), um das Fett bei der Verwendung zu reduzieren. – si618

+0

Sie bringen einen guten Punkt. Ich habe immer Strukturen verwendet, um diese Art von Dingen zu repräsentieren, obwohl ich finde, dass viele Leute sagen, Klassen zu verwenden. Aber ich frage mich, wo Strukturen in den heutigen Anwendungsentwicklungsraum passen. Googeln!! –

+0

Ich stimme der Objektinitialisierungssyntax zu, aber er hat in seiner Frage keine Sprache angegeben, und ich wollte mit der Syntax keinen zu großen Kurvenball hineinwerfen. Beachten Sie den Haftungsausschluss :) –

1

Dieser Ansatz hat gewisse Nachteile, sondern ein Weg, um die Argumentliste zu vereinfachen ist eine „Argument“ Klasse zu erstellen, die Eigenschaften hat, alle Parameter zu halten, die in übergeben werden können.

Zum Beispiel (C#)

public class InterestCalculationArguments // or maybe a struct... 
{ 
    public EClientType ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    // etc. 
} 

eine nette Sache bei diesem Ansatz ist, dass Sie die Argumente Klasse Unterklasse können die Argumente zu erweitern, die in übergeben werden können, oder Sie können auch eine „Namevaluecollection“ (zB eine String-string Karte) in der: Erlauben Sie dem Aufrufer, zusätzliche Argumente zu übergeben, die die Methode verarbeiten kann. Sie können den gleichen Ansatz für das Rückgabeobjekt verwenden, eine Klasse, die verschiedene Rückgabewerte enthält. Dieser Ansatz kann den Aufrufer auch von kleinen Änderungen an den Argumenten/Rückgabeobjekten isolieren.

+0

gute Idee! Ich werde es jetzt verwenden – chester89

2

Eine Alternative zu „Argumente Struktur“ Ansatz, die oft besser in komplizierten Fällen funktionieren, ist die Logik einer großen Methode, um eine eigene Klasse zu verschieben:

InterestRateCalculator rateCalc = new InterestRateCalculator(); 
rateCalc.ClientType = ... 
rateCalc.TotalAmount = ... 
rateCalc.CalculateSomeComplicatedInterestRate(); 

Es kann mit einer Art kombiniert werden Fabrik:

InterestRateCalculator myCalc 
    = sharedCalc.MakeMeAnInterestRateCalculator(); 

Und da Sie Überlastung Funktion erwähnten Argumente Liste zu vereinfachen, können Sie manchmal boolean Argumente mit Klassenvererbung ersetzen.

4

Split komplizierte Methoden in getrennte Klassen und erstellen Sie Instanzeigenschaften für die entsprechenden Parameter (in Ihrem Beispiel: Client, useTaxes, doYearlySummary und doNotReloadClient).

Ich kann Robert C. Martins "Clean Code: A Handbook of Agile Software Craftsmanship" sehr empfehlen, die eine sehr nette Einleitung in das Schaffen des sauberen Codes gibt.

1

Manchmal ist es einfach, solche Parameterlisten in eine Klasse zu verschieben, wenn es sinnvoll ist. Zum Beispiel könnten Sie InterestRateDefinition haben, die an vielen verschiedenen Orten verwendet werden könnte.

Allerdings fördert es oft die Erstellung von Klassen, die einfach existieren, um als Parameterliste zu dienen, und die Klasse ist bedeutungslos. Ich habe das in der Vergangenheit schon ein bisschen selbst gemacht und sie neigen dazu, den Code zu durcheinander zu bringen, anstatt Klarheit zu schaffen. Heutzutage, wenn die Argumentliste sich nicht für eine Klasse eignet, mache ich die Funktion selbst zu einer Klasse.

Sie könnten also stattdessen eine InterestRateCalculator Klasse haben, die entweder eine Reihe von öffentlichen Eingaben oder Eigenschaften benötigt.Dies löst das Problem und unterhält ein Gefühl von „Code Zweck“ plus Sie können die Bereitstellung Standardwerte und alternative Möglichkeiten zu nennen Ihre Routine durch die Klasse (zB CalculateNextCoupon, CalculateAnnual etc.)

Verwandte Themen