Ich habe kürzlich eine Codebasis gesehen, in der eine Datenklasse Address
irgendwo und dann an einem anderen Ort definiert wurde:
fun Address.toAnschrift() = let { address ->
Anschrift().apply {
// mapping code here...
}
}
Ich fand es verwirrend, diese Methode nicht direkt an der Adresse zu haben. Gibt es etablierte Muster oder Best Practices für die Verwendung von Erweiterungsmethoden? Oder muss das Buch darüber noch geschrieben werden?
Beachten Sie, dass ich trotz der Kotlin-Syntax in meinem Beispiel an allgemeinen Best Practices interessiert bin, da diese auch für C # oder andere Sprachen mit diesen Funktionen gelten würden.
Antworten:
Erweiterungsmethoden bieten nichts, was auf andere Weise nicht möglich ist. Sie sind syntaktischer Zucker, um die Entwicklung zu verbessern, ohne einen tatsächlichen technischen Nutzen zu bieten.
Erweiterungsmethoden sind relativ neu. Standards und Konventionen werden normalerweise von der Meinung der Bevölkerung festgelegt (plus langjähriger Erfahrung darüber, was letztendlich eine schlechte Idee ist), und ich glaube nicht, dass wir an dem Punkt angelangt sind, an dem wir eine endgültige Linie ziehen können, der alle zustimmen.
Ich kann jedoch mehrere Argumente sehen, die auf Dingen basieren, die ich von Kollegen gehört habe.
1. Sie ersetzen normalerweise statische Methoden.
Erweiterungsmethoden basieren am häufigsten auf Dingen, die sonst statische Methoden gewesen wären, nicht auf Klassenmethoden. Sie sehen aus wie Klassenmethoden, sind es aber nicht wirklich.
Erweiterungsmethoden sollten einfach nicht als Alternative für Klassenmethoden betrachtet werden. sondern um ansonsten syntaktisch hässlichen statischen Methoden ein "Klassenverfahren" zu verleihen .
2. Wenn Ihre beabsichtigte Methode spezifisch für ein konsumierendes Projekt ist, jedoch nicht für die Quellbibliothek.
Nur weil Sie sowohl die Bibliothek als auch den Verbraucher entwickeln, bedeutet dies nicht, dass Sie bereit sind, die Logik dort zu platzieren, wo sie passt.
Zum Beispiel:
PersonDto
Klasse aus IhremDataLayer
Projekt.WebUI
Projekt möchte aPersonDto
in a konvertieren könnenPersonViewModel
.Sie können (und möchten) diese Konvertierungsmethode nicht zu Ihrem
DataLayer
Projekt hinzufügen . Da diese Methode auf dasWebUI
Projekt beschränkt ist, sollte sie sich in diesem Projekt befinden.Mit einer Erweiterungsmethode können Sie diese Methode innerhalb Ihres Projekts (und möglicher Konsumenten Ihres Projekts) global zugänglich machen, ohne dass die
DataLayer
Bibliothek sie implementieren muss.3. Wenn Sie der Meinung sind, dass Datenklassen nur Daten enthalten sollten.
Beispielsweise
Person
enthält Ihre Klasse die Eigenschaften für eine Person. Sie möchten jedoch einige Methoden haben, mit denen einige Daten formatiert werden können:Ich habe viele Mitarbeiter gesehen, die die Idee hassen, Daten und Logik in einer Klasse zu mischen. Ich bin mit einfachen Dingen wie dem Formatieren von Daten nicht ganz einverstanden, aber ich gebe zu, dass es sich im obigen Beispiel schmutzig anfühlt
Person
, wenn man sich darauf verlassen mussSecurityQuestionAnswers
.Durch das Einfügen dieser Methode in eine Erweiterungsmethode wird verhindert, dass die ansonsten reine Datenklasse beschmutzt wird.
Beachten Sie, dass dieses Argument stilistisch ist. Dies ähnelt Teilklassen. Dies hat wenig bis gar keinen technischen Vorteil, aber Sie können Code in mehrere Dateien aufteilen, wenn Sie ihn für sauberer halten (z. B. wenn viele Benutzer die Klasse verwenden, sich aber nicht um Ihre zusätzlichen benutzerdefinierten Methoden kümmern).
4. Hilfsmethoden
In den meisten Projekten neige ich dazu, Helferklassen zu haben. Hierbei handelt es sich um statische Klassen, die normalerweise eine Sammlung von Methoden zur einfachen Formatierung bereitstellen.
Beispielsweise hatte eine Firma, bei der ich arbeitete, ein bestimmtes Datums- / Uhrzeitformat, das sie verwenden wollten. Anstatt die Formatzeichenfolge überall einzufügen oder die Formatzeichenfolge zu einer globalen Variablen zu machen, habe ich mich für eine DateTime-Erweiterungsmethode entschieden:
Ist das besser als eine normale statische Hilfsmethode? Ich glaube schon. Es bereinigt die Syntax. Anstatt
Ich könnte:
Dies ähnelt einer fließenden Version derselben Syntax. Ich mag es mehr, obwohl es keinen technischen Vorteil hat, wenn man übereinander steht.
Alle diese Argumente sind subjektiv. Keiner von ihnen deckt einen Fall ab, der sonst niemals abgedeckt werden könnte.
Andererseits können wir Ihre Behauptung aufgreifen, dass sie niemals extrem notwendig sind:
Die Antwort auf diese und Ihre Frage bleibt dieselbe:
Eine besondere Erwähnung:
quelle
Ich stimme Flater zu, dass nicht genügend Zeit für die Kristallisation von Konventionen vorhanden war, aber wir haben das Beispiel der .NET Framework-Klassenbibliotheken selbst. Beachten Sie, dass LINQ-Methoden beim Hinzufügen zu .NET nicht direkt hinzugefügt wurden
IEnumerable
, sondern als Erweiterungsmethoden.Was können wir daraus ziehen? LINQ ist
A.B().C()
anstelle vonC(B(A))
), undWenn Sie alle drei Funktionen haben, sind Erweiterungsmethoden sehr sinnvoll. In der Tat würde ich argumentieren, dass wenn eine Reihe von Methoden Feature # 3 und eines der ersten beiden Features hat, Sie erwägen sollten, sie Erweiterungsmethoden zu machen.
Erweiterungsmethoden:
null
Werten, da eine von einemnull
Wert aufgerufene Erweiterungsmethode einfach dasnull
erste Argument erhält.Erweiterungsmethoden haben einen weiteren Vorteil: Sie können weiterhin als statische Methode verwendet und direkt als Wert an eine Funktion höherer Ordnung übergeben werden. Wenn
MyMethod
es sich also um eine Erweiterungsmethode handelt,myList.Select(x => x.MyMethod())
können Sie sie einfach verwendenmyList.Select(MyMethod)
. Ich finde das schöner.quelle
Die treibende Kraft hinter Erweiterungsmethoden ist die Fähigkeit, allen Klassen oder Schnittstellen eines bestimmten Typs Funktionen hinzuzufügen, unabhängig davon, ob sie versiegelt sind oder sich in einer anderen Baugruppe befinden. Sie können dies anhand von LINQ-Code nachweisen, indem Sie allen
IEnumerable<T>
Objekten Funktionen hinzufügen , unabhängig davon, ob es sich um Listen oder Stapel usw. handelt. Das Konzept bestand darin, die Funktionen zukunftssicher zu machen, sodass SieIEnumerable<T>
LINQ automatisch verwenden können , wenn Sie eigene Funktionen entwickeln .Die Sprachfunktion ist jedoch so neu, dass Sie keine Richtlinien haben, wann Sie sie verwenden oder nicht verwenden sollen. Sie ermöglichen jedoch eine Reihe von Dingen, die bisher nicht möglich waren:
Um fair zu sein, wird nur die Zeit zeigen, wie weit zu weit ist oder an welchem Punkt Erweiterungsmethoden eher ein Hindernis als ein Segen werden. Wie in einer anderen Antwort erwähnt, enthält eine Erweiterungsmethode nichts, was mit einer statischen Methode nicht möglich wäre. Bei vernünftiger Verwendung können sie den Code jedoch leichter lesbar machen.
Was ist mit der absichtlichen Verwendung von Erweiterungsmethoden in derselben Assembly?
Ich denke, es ist wertvoll, die Kernfunktionalität gut getestet und vertrauenswürdig zu haben und dann erst dann damit herumzuspielen, wenn Sie es unbedingt müssen. Neuer Code, der davon abhängt, aber nur zum Nutzen des neuen Codes Funktionen bereitstellen muss, kann mithilfe von Erweiterungsmethoden Funktionen hinzufügen, die den neuen Code unterstützen. Diese Funktionen sind nur für den neuen Code von Interesse, und der Umfang der Erweiterungsmethoden ist auf den Namespace beschränkt, in dem sie deklariert sind.
Ich würde die Verwendung von Erweiterungsmethoden nicht blind ausschließen, aber ich würde überlegen, ob die neue Funktionalität wirklich zu dem vorhandenen Kerncode hinzugefügt werden sollte, der gut getestet ist.
quelle
Einer der häufigsten Gründe für die Verwendung von Erweiterungsmethoden ist das "Erweitern, nicht Ändern" von Schnittstellen. Anstatt zu haben
IFoo
undIFoo2
, wo letztere eine neue Methode einführtBar
,Bar
wirdIFoo
über eine Erweiterungsmethode hinzugefügt .Einer der Gründe dafür verwendet Linq - Erweiterungsmethoden für
Where
,Select
etc ist , um Benutzern zu ermöglichen , ihre eigenen Versionen dieser Methoden zu definieren, die dann auch von der Linq Abfragesyntax verwendet werden.Darüber hinaus ist der Ansatz, den ich verwende und der zu dem zu passen scheint, was ich im .NET Framework normalerweise sehe, zumindest:
Also , wenn ich eine habe
Option<T>
, würde ich die Dinge wieHasValue
,Value
,==
usw. innerhalb des Typs selbst. Aber so etwas wie eineMap
Funktion, die die Monade vonT1
in konvertiertT2
, würde ich eine Erweiterungsmethode einfügen:quelle
One of the reasons Linq uses extension methods for Where, Select etc is in order to allow users to define their own versions of these methods, which are then used by the Linq query syntax too.
-- Bist du dir da sicher? Die Linq-Syntax verwendet in großem Umfang Lambda-Ausdrücke (dh erstklassige Funktionen), sodass Sie keine eigenen Versionen vonSelect
und schreiben müssenWhere
. Ich kenne keine Fälle, in denen Leute ihre eigenen Versionen dieser Funktionen rollen.if (!predicate(item))
Code in diesem Code aufif (predicate(item))
ändern, ändern sich die Ergebnisse, da meine Version von verwendet wirdWhere
.