2010-03-16 4 views
5

Ich würde gerne Ihre Meinung dazu hören, wie weit man mit side-effect-freien Setter gehen soll.Ansatz zu seiteneffektenfreien Setzern

Betrachten Sie das folgende Beispiel:

Activity activity; 
activity.Start = "2010-01-01"; 
activity.Duration = "10 days"; // sets Finish property to "2010-01-10" 

Beachten Sie, dass für Datum und die Dauer angegebenen Werte sind nur für indikative Zwecke gezeigt.

So Setter für eine der Eigenschaften Start, Finish und Duration wird folglich andere Eigenschaften ändern und kann daher nicht als seiteneffektfrei angesehen werden. Gleiches gilt für Instanzen der Klasse Rectangle, bei der der Setzer für X die Werte Top und Bottom und so weiter ändert.

Die Frage ist, wo würden Sie eine Linie zwischen der Verwendung von Setter, die Nebenwirkungen von sich ändernden Werten logisch zusammenhängender Eigenschaften haben, und die Verwendung von Methoden, die sowieso nicht viel mehr beschreibend sein könnte. Wenn Sie beispielsweise eine Methode mit dem Namen SetDurationTo(Duration duration) definieren, wird nicht berücksichtigt, dass entweder Start oder Ende geändert werden.

Antwort

8

Ich glaube, Sie verstehen den Begriff "Nebeneffekt" falsch, wie es für das Programmdesign gilt. Einstellen einer Eigenschaft ist ein Nebeneffekt, egal wie viel oder wie wenig interner Zustand es ändert, solange es sich ändert einige Art von Zustand. Ein "seiteneffektfreier Setter" wäre nicht sehr nützlich.

Nebenwirkungen sind etwas, das Sie auf Eigenschaft vermeiden möchten Getters. Das Lesen des Werts einer Eigenschaft ist etwas, das der Aufrufer nicht erwartet, irgendeinen Zustand zu ändern (d. H. Nebeneffekte verursachen), wenn dies der Fall ist, ist es normalerweise falsch oder zumindest fraglich (es gibt Ausnahmen wie z. B. lazy loading). Aber Getter und Setter sind sowieso nur Wrapper für Methoden. Die Duration Eigenschaft, soweit es die CLR betrifft, ist nur syntaktischer Zucker für eine set_Duration Methode.

Dies ist genau, was Abstraktionen wie Klassen gemeint sind - bieten grobkörnige Operationen und behalten einen konsistenten internen Zustand. Wenn Sie absichtlich versuchen, mehrere Nebenwirkungen in einer einzigen Eigenschaftszuweisung zu vermeiden, sind Ihre Klassen letztlich nicht mehr als dumme Datencontainer.

Also, die Frage direkt beantworten: Wo zeichne ich die Linie? Nirgendwo, solange die Methode/Eigenschaft tatsächlich das tut, was der Name impliziert. Wenn das Setzen der Duration auch die ActivityName geändert hat, könnte das ein Problem sein. Wenn es die Finish Eigenschaft ändert, sollte das offensichtlich sein; es sollte unmöglich sein, die Duration zu ändern und sowohl die Start und Finish bleiben die gleichen. Die grundlegende Prämisse von OOP ist, dass Objekte intelligent genug sind, um diese Operationen selbst zu verwalten.

Wenn Sie dies konzeptionell stört, dann haben Sie keine Mutator-Eigenschaften - verwenden Sie eine unveränderbare Datenstruktur mit schreibgeschützten Eigenschaften, in der alle notwendigen Argumente im Konstruktor bereitgestellt werden. Dann haben Sie zwei Überladungen, eine, die eine Start/Duration und eine andere, die eine Start/Finish dauert. Oder machen Sie nur eine der Eigenschaften beschreibbar - sagen wir Finish, um sie mit Start konsistent zu halten - und machen Sie dann schreibgeschützt. Verwenden Sie die geeignete Kombination von veränderbaren und unveränderlichen Eigenschaften, um sicherzustellen, dass es nur eine Möglichkeit gibt, einen bestimmten Status zu ändern.

Andernfalls sorgen Sie sich nicht so viel darum. Eigenschaften (und Methoden) sollten nicht unbeabsichtigte oder undokumentierte Nebenwirkungen haben, aber das ist über die einzige Richtlinie, die ich verwenden würde.

+0

Danke, das habe ich gesucht und es kommt mir nie der "Nebeneffekt" vor "Freier Setter" ist in Wirklichkeit nicht möglich, da er den Zustand verändern wird. Und wie Sie darauf hingewiesen haben, wird die CLR trotzdem zur Methode übersetzen. – Martin

+0

Ja, der Nebeneffekt eines Setter besteht darin, * die Member-Variable zu setzen, auf die er verweist *.Ein zusätzlicher Nebeneffekt besteht jedoch darin, * andere * Membervariablen zu modifizieren. –

+0

Ich habe gerade über das gleiche nachgedacht und ich würde sogar sagen, dass die Einstellung der Elementvariable kein Nebeneffekt, sondern der gewünschte Effekt des Setter ist. Das Ändern anderer Elementvariablen ist ein Nebeneffekt. – Martin

0

Ich habe immer mit der allgemeinen Regel gearbeitet, public Setter auf Eigenschaften zu erlauben, die nicht frei von Nebenwirkungen sind, da Anrufer Ihrer öffentlichen Setter nicht sicher sein können, was passieren könnte, aber natürlich Leute, die das modifizieren Assembly selbst sollte eine ziemlich gute Idee haben, da sie den Code sehen können.

Natürlich gibt es immer Zeiten, in denen Sie die Regel aus Gründen der Lesbarkeit brechen müssen, um Ihr Objektmodell logisch zu machen, oder einfach um die Dinge richtig zu machen. Wie Sie gesagt haben, wirklich eine Frage der Präferenz im Allgemeinen.

1

Persönlich halte ich es für sinnvoll, einen Nebeneffekt zu haben, um einen konsistenten Zustand zu erhalten. Wie Sie sagten, ist es sinnvoll, logisch zusammenhängende Werte zu ändern. In gewissem Sinne wird der Nebeneffekt erwartet. Aber das Wichtigste ist, diesen Punkt klarzustellen. Das heißt, es sollte offensichtlich sein, dass die Aufgabe, die die Methode ausführt, irgendeine Art von Nebeneffekt hat. Also statt könnten Sie Ihre Funktion ChangeDurationTo aufrufen, was impliziert, dass noch etwas vor sich geht. Sie können dies auch auf andere Weise tun, indem Sie eine Funktion/Methode verwenden, die die Dauer AdjustDurationTo anpasst und einen delta Wert übergibt. Es würde helfen, wenn Sie die Funktion als Nebeneffekt dokumentieren.

Ich denke, ein anderer Weg, um es zu betrachten, ist zu sehen, ob eine Nebenwirkung erwartet wird. In Ihrem Beispiel eines Rectangle würde ich erwarten, dass es die Werte von top oder bottom ändert, um einen intern konsistenten Zustand beizubehalten. Ich weiß nicht, ob das subjektiv ist; es scheint nur Sinn für mich zu machen. Wie immer denke ich, dass Dokumentation gewinnt. Wenn es einen Nebeneffekt gibt, dokumentieren Sie es wirklich gut. Vorzugsweise mit dem Namen der Methode und durch unterstützende Dokumentation.

-2

Ich denke, es ist vor allem eine Frage des gesunden Menschenverstands. In diesem speziellen Beispiel ist mein Problem nicht so sehr, dass Sie Eigenschaften haben, die "verwandte" Eigenschaften anpassen, sondern dass Sie Eigenschaften haben, die Zeichenfolgenwerte nehmen, die Sie intern in DateTime (oder was auch immer) analysieren) Werte.

Ich würde viel lieber etwas sehen:

Activity activity; 
activity.Start = DateTime.Parse("2010-01-01"); 
activity.Duration = Duration.Parse("10 days"); 

Das heißt, Sie explicity beachten Sie, dass Sie das Parsen von Strings tun.Erlaube dem Programmierer, auch stark typisierte Objekte anzugeben, wenn dies angemessen ist.

+0

Wie ich bereits erwähnt habe "Beachten Sie, dass Werte für Datum und Dauer nur zu Richtzwecken angezeigt werden." Dein Kommentar geht also wirklich nicht auf die gestellte Frage ein, aber danke trotzdem ... Ich wusste, dass jemand darauf zeigen wird ;-) – Martin

1

Eine Option besteht darin, Ihre Klasse unveränderbar zu machen und Methoden zu erstellen und neue Instanzen der Klasse zurückzugeben, bei denen alle entsprechenden Werte geändert wurden. Dann gibt es keine Nebenwirkungen oder Setter. Denken Sie an etwas wie DateTime, wo Sie Dinge wie AddDays und AddHours aufrufen können, die eine neue DateTime Instanz mit der angewandten Änderung zurückgeben werden.