Ich habe eine Klasse namens Heading
, die ein paar Dinge tut, aber sie sollte auch das Gegenteil des aktuellen Heading-Werts zurückgeben können, der schließlich verwendet werden muss, indem eine neue Instanz der Heading
Klasse selbst erstellt wird.
Ich kann eine einfache Eigenschaft haben, die aufgerufen wird reciprocal
, um die entgegengesetzte Überschrift des aktuellen Werts zurückzugeben und dann manuell eine neue Instanz der Heading-Klasse zu erstellen, oder ich kann eine Methode createReciprocalHeading()
erstellen, die automatisch eine neue Instanz der Heading-Klasse erstellt und an die zurückgibt Benutzer.
Allerdings ist einer meiner Kollegen empfahl mich nur eine Klasse erstellen Eigenschaft genannt , reciprocal
dass gibt eine neue Instanz der Klasse selbst über seine Getter - Methode.
Meine Frage ist: Ist es nicht ein Anti-Pattern für eine Eigenschaft einer Klasse, sich so zu verhalten?
Ich finde das besonders weniger intuitiv, weil:
- Meiner Meinung nach sollte eine Eigenschaft einer Klasse keine neue Instanz einer Klasse zurückgeben
- Der Name der Eigenschaft
reciprocal
hilft dem Entwickler nicht, ihr Verhalten vollständig zu verstehen, ohne Hilfe von der IDE zu erhalten oder die Getter-Signatur zu überprüfen.
Bin ich zu streng darüber, was eine Klasseneigenschaft tun soll, oder ist es ein berechtigtes Anliegen? Ich habe immer versucht, den Status der Klasse über ihre Felder und Eigenschaften und ihr Verhalten über ihre Methoden zu verwalten, und ich verstehe nicht, wie dies zur Definition der Klasseneigenschaft passt.
quelle
Heading
unveränderlichen Typ zu haben undreciprocal
einen neuen zurückzugebenHeading
. (mit der Einschränkung bei den beiden Aufrufenreciprocal
sollte "das Gleiche" zurückgeben, dh sie sollten den Gleichheitstest bestehen.)Antworten:
Es ist nicht unbekannt, Dinge wie Copy () oder Clone () zu haben, aber ich denke, Sie haben Recht, sich um diese zu sorgen.
Nehmen Sie zum Beispiel:
Es wäre schön, eine Warnung zu haben, dass die Eigenschaft jedes Mal ein neues Objekt war.
Sie könnten auch annehmen, dass:
Jedoch. Wenn Ihre Überschriftenklasse ein Typ mit unveränderlichem Wert wäre, könnten Sie diese Beziehungen implementieren, und umgekehrt könnte ein guter Name für die Operation sein
quelle
Copy()
undClone()
Methoden sind, keine Eigenschaften. Ich glaube, das OP bezieht sich auf Eigenschafts-Getter, die neue Instanzen zurückgeben.String.Substring()
,Array.Reverse()
,Int32.GetHashCode()
etc.If your heading class was an immutable value type
- Ich denke, Sie sollten hinzufügen, dass Eigenschaftssetzer für unveränderliche Objekte immer die neuen Instanzen zurückgeben sollten (oder zumindest, wenn der neue Wert nicht mit dem alten Wert übereinstimmt), da dies die Definition für unveränderliche Objekte ist. :)Eine Schnittstelle einer Klasse veranlasst den Benutzer der Klasse, Annahmen darüber zu treffen, wie sie funktioniert.
Wenn viele dieser Annahmen richtig und wenige falsch sind, ist die Schnittstelle gut.
Wenn viele dieser Annahmen falsch und wenige richtig sind, ist die Schnittstelle Müll.
Eine verbreitete Annahme zu Properties ist, dass das Aufrufen der Funktion get billig ist. Eine weitere verbreitete Annahme zu Eigenschaften ist, dass das zweimalige Aufrufen der get-Funktion in Folge dasselbe zurückgibt.
Sie können dies umgehen, indem Sie die Konsistenz verwenden, um die Erwartungen zu ändern. Zum Beispiel für eine kleine 3D-Bibliothek, wo Sie brauchen
Vector
,Ray
Matrix
usw. Sie können es so machen, dass Getter wieVector.normal
undMatrix.inverse
so ziemlich immer teuer sind. Leider , auch wenn Ihre Schnittstellen konsequent teuer Eigenschaften verwenden, werden die Schnittstellen bestenfalls so intuitiv wie man , dass AnwendungenVector.create_normal()
undMatrix.create_inverse()
- aber ich kenne kein starkes Argument , die gemacht werden können , dass Eigenschaften schaffen eine intuitive Schnittstelle, auch nach den Erwartungen ändern.quelle
Eigenschaften sollten sehr schnell zurückkehren, bei wiederholten Aufrufen denselben Wert zurückgeben und das Abrufen des Werts sollte keine Nebenwirkungen haben. Was Sie hier beschreiben, sollte nicht als Eigenschaft implementiert werden.
Ihre Intuition ist weitgehend richtig. Eine Factory-Methode gibt bei wiederholten Aufrufen nicht denselben Wert zurück. Es wird jedes Mal eine neue Instanz zurückgegeben. Dies disqualifiziert es so ziemlich als Eigentum. Es ist auch nicht schnell, wenn eine spätere Entwicklung der Instanzerstellung Gewicht verleiht, z. B. Netzwerkabhängigkeiten.
Eigenschaften sollten im Allgemeinen sehr einfache Operationen sein, die einen Teil des aktuellen Status der Klasse abrufen / festlegen. Fabrikähnliche Vorgänge erfüllen diese Kriterien nicht.
quelle
DateTime.Now
Eigenschaft wird häufig verwendet und verweist auf ein neues Objekt. Ich denke, der Name einer Eigenschaft kann dabei helfen, die Unklarheit zu beseitigen, ob jedes Mal dasselbe Objekt zurückgegeben wird oder nicht. Es ist alles im Namen und wie es verwendet wird.DateTime
es sich um einen Werttyp handelt und kein Konzept der Objektidentität vorliegt. Wenn ich das richtig verstehe, ist das Problem, dass es nicht deterministisch ist und jedes Mal einen neuen Wert zurückgibt.DateTime.New
ist statisch, und daher können Sie nicht erwarten, dass es einen Zustand eines Objekts darstellt.Ich glaube nicht, dass es eine sprachunabhängige Antwort darauf gibt, da das, was ein „Eigentum“ ausmacht, eine sprachspezifische Frage ist und was ein Anrufer eines „Eigentums“ erwartet, auch eine sprachspezifische Frage ist. Ich denke, die fruchtbarste Art, darüber nachzudenken, besteht darin, darüber nachzudenken, wie es aus Sicht des Anrufers aussieht.
In C # unterscheiden sich Eigenschaften dadurch, dass sie (konventionell) groß geschrieben werden (wie Methoden), aber keine Klammern (wie öffentliche Instanzvariablen) enthalten. Was erwarten Sie, wenn der folgende Code ohne Dokumentation angezeigt wird?
Als relativer C # -Anfänger, der jedoch die Microsoft Property Usage Guidelines gelesen hat , würde ich
Reciprocal
unter anderem Folgendes erwarten :Heading
Klasse seinReciprocalChanged
Veranstaltung anbietenVon diesen Annahmen sind (3) und (4) wahrscheinlich richtig (unter der Annahme, dass
Heading
es sich um einen unveränderlichen Wertetyp handelt, wie in Ewans Antwort ), (1) ist umstritten, (2) ist unbekannt, aber auch umstritten, und (5) ist unwahrscheinlich machen semantischen Sinn (obwohl , was hat eine Überschrift vielleicht haben sollteHeadingChanged
Ereignis). Dies legt für mich den Schluss nahe, dass in einer C # -API "Get or Calculate the Reciprocal" nicht als Eigenschaft implementiert werden sollte. Insbesondere, wenn die Berechnung billig undHeading
unveränderlich ist, handelt es sich um einen Grenzfall.(Beachten Sie jedoch, dass keine dieser Bedenken damit zu tun hat, ob durch den Aufruf der Eigenschaft eine neue Instanz erstellt wird , nicht einmal (2). Das Erstellen von Objekten in der CLR an sich ist recht kostengünstig.)
In Java sind Eigenschaften eine Namenskonvention für Methoden. Wenn ich sehe
Meine Erwartungen sind ähnlich wie die oben genannten (wenn auch weniger explizit dargelegt): Ich erwarte, dass der Anruf billig, idempotent und ohne Nebenwirkungen ist. Außerhalb des JavaBeans-Frameworks ist das Konzept einer "Eigenschaft" in Java jedoch nicht so aussagekräftig, und insbesondere, wenn eine unveränderliche Eigenschaft ohne Entsprechung betrachtet wird
setReciprocal()
, ist diegetXXX()
Konvention inzwischen etwas altmodisch. Aus Effective Java , zweite Ausgabe (bereits über acht Jahre alt):Eine zeitgemäße, flüssigere API würde ich also erwarten
- was wiederum darauf hindeuten würde, dass der Anruf billig, idempotent und ohne Nebenwirkungen ist, aber nichts darüber aussagen würde, ob eine neue Berechnung durchgeführt oder ein neues Objekt erstellt wird. Das ist in Ordnung; In einer guten API sollte es mich nicht interessieren.
In Ruby gibt es keine Immobilien. Es gibt "Attribute", aber wenn ich sehe
Ich habe keine unmittelbare Möglichkeit zu wissen, ob ich
@reciprocal
über eineattr_reader
oder eine einfache Accessormethode auf eine Instanzvariable zugreife oder ob ich eine Methode aufrufe, die eine teure Berechnung durchführt. Die Tatsache, dass der Methodenname ein einfaches Substantiv istcalcReciprocal
, lässt jedoch nicht sagen , dass der Aufruf zumindest billig ist und wahrscheinlich keine Nebenwirkungen hat.In Scala ist die Namenskonvention, dass Methoden mit Nebenwirkungen Klammern verwenden und Methoden ohne Klammern
könnte sein:
(Beachten Sie, dass Scala ermöglicht verschiedene Dinge , die dennoch sind entmutigt durch den Style - Guide . Dies ist eine meiner großen Belästigungen mit Scala ist.)
Das Fehlen von Klammern sagt mir, dass der Anruf keine Nebenwirkungen hat. der name legt wiederum nahe, dass der anruf relativ billig sein sollte. Darüber hinaus ist es mir egal, wie es mir den Wert bringt.
Kurz gesagt: Kennen Sie die Sprache, die Sie verwenden, und wissen Sie, welche Erwartungen andere Programmierer an Ihre API stellen. Alles andere ist ein Implementierungsdetail.
quelle
Wie andere bereits gesagt haben, ist es ein weit verbreitetes Muster, Instanzen derselben Klasse zurückzugeben.
Die Benennung sollte mit den Benennungskonventionen der Sprache einhergehen.
Zum Beispiel würde ich in Java wahrscheinlich erwarten, dass es aufgerufen wird
getReciprocal();
Davon abgesehen würde ich als veränderlich gegenüber unveränderlichen Objekten betrachten.
Bei unveränderlichen sind die Dinge ziemlich einfach, und es tut nicht weh, wenn Sie dasselbe Objekt zurückgeben oder nicht.
Bei veränderlichen kann dies sehr beängstigend werden.
Worauf bezieht sich b nun? Der Kehrwert des ursprünglichen Wertes von a? Oder das Gegenteil des Veränderten? Dies mag ein zu kurzes Beispiel sein, aber Sie verstehen es.
Suchen Sie in diesen Fällen nach einer besseren Benennung, die mitteilt, was passiert
createReciprocal()
.Aber es kommt auch wirklich auf den Kontext an.
quelle
getReciprocal()
, da dies wie ein "normaler" JavaBeans-Style-Getter "klingt". Bevorzugen Sie "createXXX ()" oder möglicherweise "berechneXXX ()", die anderen Programmierern anzeigen oder andeuten, dass etwas anderes passiertIm Interesse der Einfachverantwortung und Klarheit hätte ich eine ReverseHeadingFactory, die das Objekt entgegennimmt und seine Rückseite zurückgibt. Dies würde klarer machen, dass ein neues Objekt zurückgegeben wird, und bedeuten, dass der Code zur Erzeugung der Umkehrung nicht in anderem Code enthalten ist.
quelle
Es hängt davon ab, ob. Normalerweise erwarte ich, dass Eigenschaften Dinge zurückgeben, die Teil einer Instanz sind, daher wäre es etwas ungewöhnlich, eine andere Instanz derselben Klasse zurückzugeben.
Eine String-Klasse könnte beispielsweise die Eigenschaften "firstWord", "lastWord" oder, wenn sie Unicode "firstLetter", "lastLetter" verarbeitet, vollständige String-Objekte haben - normalerweise nur kleinere.
quelle
Da die anderen Antworten den Eigenschaftsteil abdecken, werde ich nur Ihre Nummer 2 zu
reciprocal
folgenden Themen ansprechen :Verwenden Sie nichts anderes als
reciprocal
. Es ist der einzig richtige Begriff für das, was Sie beschreiben (und es ist der formale Begriff). Schreiben Sie keine falsche Software, um Ihren Entwicklern das Erlernen der Domäne, in der sie arbeiten, zu ersparen. In der Navigation haben Begriffe sehr spezifische Bedeutungen und verwenden etwas, das scheinbar harmlos istreverse
oderopposite
in bestimmten Kontexten zu Verwirrung führen kann.quelle
Logischerweise ist es keine Eigenschaft einer Überschrift. Wenn es so wäre, könnte man auch sagen, dass das Negativ einer Zahl eine Eigenschaft dieser Zahl ist, und ehrlich gesagt würde dies dazu führen, dass "Eigenschaft" jede Bedeutung verliert, fast alles wäre eine Eigenschaft.
Das Reziproke ist eine reine Funktion einer Überschrift, genauso wie das Negative einer Zahl eine reine Funktion dieser Zahl ist.
Als Faustregel gilt, dass eine Einstellung, die keinen Sinn ergibt, wahrscheinlich keine Eigenschaft sein sollte. Das Einstellen kann natürlich immer noch nicht zugelassen werden, aber das Hinzufügen eines Einstellers sollte theoretisch möglich und sinnvoll sein.
In einigen Sprachen ist es möglicherweise sinnvoll, sie als eine im Kontext dieser Sprache angegebene Eigenschaft zu haben, wenn sie aufgrund der Mechanik dieser Sprache einige Vorteile bietet. Wenn Sie es beispielsweise in einer Datenbank speichern möchten, ist es wahrscheinlich sinnvoll, es als Eigenschaft zu definieren, wenn es die automatische Verarbeitung durch das ORM-Framework ermöglicht. Oder vielleicht ist es eine Sprache, in der es keinen Unterschied zwischen einer Eigenschaft und einer parameterlosen Elementfunktion gibt (ich kenne eine solche Sprache nicht, aber ich bin sicher, dass es einige gibt). Dann ist es Sache des API-Designers und -Dokumentators, zwischen Funktionen und Eigenschaften zu unterscheiden.
Bearbeiten: Für C # würde ich mich mit vorhandenen Klassen befassen, insbesondere mit denen der Standardbibliothek.
BigInteger
undComplex
Strukturen. Sie sind jedoch Strukturen und haben nur statische Funktionen, und alle Eigenschaften sind von unterschiedlichem Typ. Also nicht viel Designhilfe für euch daHeading
Klasse.Vector
Klasse , die nur sehr wenige Eigenschaften hat und Ihrer sehr nahe zu sein scheintHeading
. Wenn Sie so vorgehen, dann haben Sie eine Funktion für Gegenseitigkeit und keine Eigenschaft.Möglicherweise möchten Sie Bibliotheken anzeigen, die tatsächlich von Ihrem Code verwendet werden, oder vorhandene ähnliche Klassen. Versuchen Sie konsequent zu bleiben, das ist normalerweise das Beste, wenn ein Weg nicht eindeutig richtig und ein anderer falsch ist.
quelle
Sehr interessante Frage. Ich sehe keine SOLID + DRY + KISS-Verletzung in dem, was du vorschlägst, aber trotzdem riecht es schlecht.
Eine Methode, die eine Instanz der Klasse zurückgibt, heißt Konstruktor , oder? Sie erstellen also eine neue Instanz mit einer Nicht-Konstruktormethode. Es ist nicht das Ende der Welt, aber als Kunde würde ich das nicht erwarten . Dies geschieht normalerweise unter bestimmten Umständen: einer Factory oder manchmal einer statischen Methode derselben Klasse (nützlich für Singletons und für ... nicht so objektorientierte Programmierer?)
Plus: Was passiert, wenn Sie
getReciprocal()
das zurückgegebene Objekt aufrufen ? Sie erhalten eine weitere Instanz der Klasse, die eine genaue Kopie der ersten sein könnte! Und wenn Sie sichgetReciprocal()
auf DIESES berufen? Gegenstände, die irgendjemanden ausbreiten?Nochmals: Was ist, wenn der Aufrufer den reziproken Wert benötigt (als Skalar meine ich)?
getReciprocal()->getValue()
? Wir verletzen das Gesetz von Demeter wegen geringer Gewinne.quelle