Ich denke, die gebräuchlichste Add
Methode, einer Sammlung etwas hinzuzufügen, ist die Verwendung einer Methode, die eine Sammlung bietet:
class Item {}
var items = new List<Item>();
items.Add(new Item());
und daran ist eigentlich nichts ungewöhnliches.
Ich frage mich jedoch, warum wir das nicht so machen:
var item = new Item();
item.AddTo(items);
es scheint irgendwie natürlicher zu sein als die erste Methode. Dies hätte den Vorteil, dass, wenn die Item
Klasse eine Eigenschaft wie folgt hat Parent
:
class Item
{
public object Parent { get; private set; }
}
Sie können den Setter privat machen. In diesem Fall können Sie natürlich keine Erweiterungsmethode verwenden.
Aber vielleicht irre ich mich und habe dieses Muster noch nie gesehen, weil es so ungewöhnlich ist? Wissen Sie, ob es ein solches Muster gibt?
In C#
einer Erweiterungsmethode wäre das sinnvoll
public static T AddTo(this T item, IList<T> list)
{
list.Add(item);
return item;
}
Wie wäre es mit anderen Sprachen? Ich denke, in den meisten von ihnen musste die Item
Klasse eine ICollectionItem
Schnittstelle bereitstellen, die wir sie nennen .
Update-1
Ich habe ein bisschen mehr darüber nachgedacht und dieses Muster wäre zum Beispiel wirklich nützlich, wenn Sie nicht möchten, dass ein Artikel mehreren Sammlungen hinzugefügt wird.
Testschnittstelle ICollectable
:
interface ICollectable<T>
{
// Gets a value indicating whether the item can be in multiple collections.
bool CanBeInMultipleCollections { get; }
// Gets a list of item's owners.
List<ICollection<T>> Owners { get; }
// Adds the item to a collection.
ICollectable<T> AddTo(ICollection<T> collection);
// Removes the item from a collection.
ICollectable<T> RemoveFrom(ICollection<T> collection);
// Checks if the item is in a collection.
bool IsIn(ICollection<T> collection);
}
und eine Beispielimplementierung:
class NodeList : List<NodeList>, ICollectable<NodeList>
{
#region ICollectable implementation.
List<ICollection<NodeList>> owners = new List<ICollection<NodeList>>();
public bool CanBeInMultipleCollections
{
get { return false; }
}
public ICollectable<NodeList> AddTo(ICollection<NodeList> collection)
{
if (IsIn(collection))
{
throw new InvalidOperationException("Item already added.");
}
if (!CanBeInMultipleCollections)
{
bool isInAnotherCollection = owners.Count > 0;
if (isInAnotherCollection)
{
throw new InvalidOperationException("Item is already in another collection.");
}
}
collection.Add(this);
owners.Add(collection);
return this;
}
public ICollectable<NodeList> RemoveFrom(ICollection<NodeList> collection)
{
owners.Remove(collection);
collection.Remove(this);
return this;
}
public List<ICollection<NodeList>> Owners
{
get { return owners; }
}
public bool IsIn(ICollection<NodeList> collection)
{
return collection.Contains(this);
}
#endregion
}
Verwendung:
var rootNodeList1 = new NodeList();
var rootNodeList2 = new NodeList();
var subNodeList4 = new NodeList().AddTo(rootNodeList1);
// Let's move it to the other root node:
subNodeList4.RemoveFrom(rootNodeList1).AddTo(rootNodeList2);
// Let's try to add it to the first root node again...
// and it will throw an exception because it can be in only one collection at the same time.
subNodeList4.AddTo(rootNodeList1);
quelle
item.AddTo(items)
Angenommen, Sie haben eine Sprache ohne Erweiterungsmethoden: natürlich oder nicht, um addTo zu unterstützen, würde jeder Typ diese Methode benötigen und für jede Art von Sammlung bereitstellen, die das Anhängen unterstützt. Das ist wie das beste Beispiel für die Einführung von Abhängigkeiten zwischen allem, was ich jemals gehört habe: P - Ich denke, die falsche Prämisse hier ist der Versuch, eine Programmierabstraktion für das „echte“ Leben zu modellieren. Das geht oft schief.add(item, collection)
, aber das ist kein guter OO-Stil.Antworten:
Nein,
item.AddTo(items)
natürlicher geht es nicht. Ich denke, Sie vermischen dies mit Folgendem:Sie haben Recht damit, dass
items.Add(item)
es der natürlichen englischen Sprache nicht sehr nahe kommt. Aber Sie hören auch nichtitem.AddTo(items)
in natürlicher englischer Sprache, oder? Normalerweise gibt es jemanden, der den Artikel zur Liste hinzufügen soll. Sei es bei der Arbeit in einem Supermarkt oder beim Kochen und Hinzufügen von Zutaten.Im Fall von Programmiersprachen haben wir es so gemacht, dass eine Liste beides tut: Speichern ihrer Elemente und dafür verantwortlich, sie sich selbst hinzuzufügen.
Das Problem bei Ihrer Vorgehensweise ist, dass ein Element wissen muss, dass eine Liste vorhanden ist. Aber ein Gegenstand könnte existieren, selbst wenn es überhaupt keine Listen gäbe, oder? Dies zeigt, dass es Listen nicht berücksichtigen sollte. Listen sollten in ihrem Code überhaupt nicht vorkommen.
Listen existieren jedoch nicht ohne Elemente (zumindest wären sie unbrauchbar). Daher ist es in Ordnung, wenn sie über ihre Artikel Bescheid wissen - zumindest in allgemeiner Form.
quelle
@valenterry hat völlig recht mit dem Problem der Trennung von Bedenken. Klassen sollten nichts über die verschiedenen Sammlungen wissen, die sie enthalten könnten. Sie müssen die Elementklasse nicht jedes Mal ändern, wenn Sie eine neue Art von Sammlung erstellen.
Das gesagt...
1. Nicht Java
Java ist für Sie hier keine Hilfe, aber einige Sprachen verfügen über eine flexiblere, erweiterbare Syntax.
In Scala sieht items.add (item) folgendermaßen aus:
Wobei :: ein Operator ist, der Elemente zu Listen hinzufügt. Das scheint genau das zu sein, was du willst. Es ist eigentlich syntaktischer Zucker für
wo :: ist eine Scala Liste Methode , die effektiv äquivalent zu Java ist list.add . So , während es in der ersten Version sieht aus , als ob Artikel selbst ist das Hinzufügen Artikel , in Wahrheit Artikeln der Zugabe tut. Beide Versionen sind gültige Scala
Dieser Trick erfolgt in zwei Schritten:
Wenn Sie mit vielen Operatoren nicht vertraut sind und Ihr AddTo benötigen , bietet Scala eine weitere Option: implizite Konvertierungen. Dies erfordert zwei Schritte
Wenn implizite Konvertierungen aktiviert sind und Scala dies sieht
oder
Wenn item kein AddTo hat , durchsucht es den aktuellen Bereich nach impliziten Konvertierungsfunktionen. Wenn eine der Umwandlungsfunktionen einen Typ zurückgibt , die tut eine haben addTo Methode, dies geschieht:
Möglicherweise entspricht der Weg impliziter Konvertierungen eher Ihrem Geschmack, erhöht jedoch die Gefahr, da Code unerwartete Aktionen ausführen kann, wenn Sie nicht alle impliziten Konvertierungen in einem bestimmten Kontext genau kennen. Aus diesem Grund ist diese Funktion in Scala standardmäßig nicht mehr aktiviert. (Während Sie entscheiden können, ob Sie es in Ihrem eigenen Code aktivieren möchten oder nicht, können Sie nichts gegen den Status in den Bibliotheken anderer tun. Hier sind Drachen).
Die durch Doppelpunkte abgeschlossenen Operatoren sind hässlicher, stellen jedoch keine Bedrohung dar.
Bei beiden Ansätzen wird das Prinzip der Trennung der Anliegen beibehalten. Sie können den Eindruck erwecken , dass der Artikel über das Sammeln Bescheid weiß, die Arbeit jedoch an einem anderen Ort ausgeführt wird. Jede andere Sprache, die Ihnen diese Funktion bieten möchte, sollte mindestens genauso vorsichtig sein.
2. Java
In Java, wo die Syntax von Ihnen nicht erweiterbar ist, können Sie am besten eine Art Wrapper / Decorator-Klasse erstellen, die sich mit Listen auskennt. In der Domäne, in der Ihre Artikel in Listen abgelegt sind, können Sie sie darin einschließen. Wenn sie diese Domain verlassen, nehmen Sie sie raus. Vielleicht ist das Besuchermuster am nächsten.
3. tl, dr
Scala kann Ihnen wie einige andere Sprachen mit einer natürlicheren Syntax behilflich sein. Java kann Ihnen nur hässliche, barocke Muster anbieten.
quelle
Bei Ihrer Erweiterungsmethode müssen Sie immer noch Add () aufrufen, damit Ihrem Code nichts Nützliches hinzugefügt wird. Sofern Ihre Add-To-Methode keine andere Verarbeitung durchgeführt hat, gibt es keinen Grund, warum sie existiert. Und das Schreiben von Code, der keinen funktionalen Zweck hat, ist schlecht für die Lesbarkeit und Wartbarkeit.
Wenn Sie es nicht generisch lassen, werden die Probleme noch offensichtlicher. Sie haben jetzt ein Element, das über verschiedene Arten von Sammlungen Bescheid wissen muss, in denen es sein kann. Dies verstößt gegen das Prinzip der einmaligen Verantwortung. Ein Objekt ist nicht nur Inhaber seiner Daten, sondern manipuliert auch Sammlungen. Aus diesem Grund behalten wir uns die Verantwortung für das Hinzufügen von Elementen zu Sammlungen bis zu dem Code vor, der beide Elemente belegt.
quelle
thing.giveTo(collection)
[und diecollection
Implementierung einer "acceptOwnership" -Schnittstelle] sinnvoller, alscollection
eine Methode einzuschließen, mit der das Eigentum ergriffen wird. Natürlich ...Ich denke, Sie sind mit dem Wort "hinzufügen" besessen. Versuchen Sie stattdessen, nach einem alternativen Wort wie "collect" zu suchen, das Ihnen eine englischfreundlichere Syntax ohne Änderung des Musters bietet:
Die obige Methode ist genau die gleiche wie
items.add(item);
, mit dem einzigen Unterschied, dass es sich um eine andere Wortwahl handelt, und sie fließt sehr schön und "natürlich" in Englisch.Manchmal verhindert das bloße Umbenennen von Variablen, Methoden, Klassen usw. das anstrengende Umgestalten des Musters.
quelle
collect
wird manchmal als Name für Methoden verwendet, die eine Auflistung von Elementen in eine Art singuläres Ergebnis reduzieren (das auch eine Auflistung sein kann). Diese Konvention wurde von der Java Steam API übernommen , was die Verwendung des Namens in diesem Zusammenhang immens populär machte. Ich habe das Wort, das Sie in Ihrer Antwort erwähnt haben, noch nie gesehen. Ichadd
put
push
oderenqueue
. Der Punkt ist nicht der spezifische Beispielname, sondern die Tatsache, dass ein anderer Name eher dem Wunsch des OP nach einer englischsprachigen Grammatik entspricht.Obwohl es für den allgemeinen Fall (wie von anderen erläutert) kein solches Muster gibt, ist der Kontext, in dem ein ähnliches Muster seine Vorzüge hat, eine bidirektionale Eins-zu-Viele-Zuordnung in ORM.
In diesem Zusammenhang hätten Sie zwei Entitäten, sagen wir
Parent
undChild
. Ein Elternteil kann viele Kinder haben, aber jedes Kind gehört einem Elternteil. Wenn die Anwendung erfordert, dass die Zuordnung von beiden Seiten navigierbar ist (bidirektional), haben Sie eineList<Child> children
in der übergeordneten Klasse und eineParent parent
in der untergeordneten Klasse.Die Assoziation sollte natürlich immer wechselseitig sein. ORM-Lösungen erzwingen dies normalerweise für Entitäten, die sie laden. Wenn die Anwendung jedoch eine Entität manipuliert (entweder persistent oder neu), muss sie dieselben Regeln durchsetzen. Nach meiner Erfahrung finden Sie am häufigsten eine
Parent.addChild(child)
Methode, die aufgerufenChild.setParent(parent)
wird und nicht für den öffentlichen Gebrauch bestimmt ist. In letzterem finden Sie normalerweise die gleichen Prüfungen, die Sie in Ihrem Beispiel implementiert haben. Die andere Route kann aber auch implementiert werden:Child.addTo(parent)
welche AnrufeParent.addChild(child)
. Welche Route die beste ist, ist von Situation zu Situation unterschiedlich.quelle
In Java, hält eine typische Sammlung nicht Dinge --Es hält Verweise auf Dinge, oder genauer gesagt Kopien von Referenzen zu den Dingen. Im Gegensatz dazu wird die Dot-Member-Syntax im Allgemeinen verwendet, um auf das durch eine Referenz identifizierte Objekt einzuwirken, und nicht auf die Referenz selbst.
Wenn Sie einen Apfel in eine Schüssel geben, ändert dies einige Aspekte des Apfels (z. B. seine Position). Wenn Sie jedoch einen Zettel mit der Aufschrift "Objekt Nr. 29521" in eine Schüssel geben, ändert dies auch dann nichts an dem Apfel, wenn dies der Fall ist Objekt # 29521. Da in Sammlungen Kopien von Referenzen enthalten sind, gibt es kein Java-Äquivalent dazu, einen Zettel mit der Aufschrift "Objekt # 29521" in eine Schüssel zu legen. Stattdessen kopiert man die Nummer 29521 auf einen neuen Zettel und gibt sie in die Schüssel, um eine eigene Kopie zu erstellen. Das Original bleibt davon unberührt.
Da Objektreferenzen (die "Zettel") in Java passiv beobachtet werden können, haben weder die Referenzen noch die dadurch identifizierten Objekte irgendeine Möglichkeit zu wissen, was mit ihnen gemacht wird. Es gibt einige Arten von Sammlungen, die nicht nur eine Reihe von Verweisen auf Objekte enthalten, die möglicherweise nichts von der Existenz der Sammlung wissen, sondern stattdessen eine bidirektionale Beziehung zu den darin eingekapselten Objekten herstellen. Bei einigen solchen Sammlungen kann es sinnvoll sein, eine Methode anzufordern, die sich selbst zu einer Sammlung hinzufügt (insbesondere, wenn ein Objekt jeweils nur zu einer solchen Sammlung gehören kann). Im Allgemeinen passen die meisten Sammlungen jedoch nicht zu diesem Muster und akzeptieren möglicherweise Verweise auf Objekte, ohne dass die durch diese Verweise identifizierten Objekte betroffen sind.
quelle
item.AddTo(items)
wird nicht verwendet, weil es passiv ist. Vgl. "Der Artikel wird zur Liste hinzugefügt" und "Der Raum wird vom Dekorateur bemalt".Passive Konstruktionen sind in Ordnung, aber zumindest auf Englisch bevorzugen wir aktive Konstruktionen.
Bei der OO-Programmierung spielt das Pardigma eine herausragende Rolle für Schauspieler, nicht für diejenigen, auf die gehandelt wurde
items.Add(item)
. Die Liste ist die Sache, die etwas tut. Es ist der Schauspieler, das ist also der natürlichere Weg.quelle
list.Add(item)
ein Element sagen, das zur Liste hinzugefügt wird oder eine Liste fügt ein Element hinzu oder überitem.AddTo(list)
das Element, das sich selbst zur Liste hinzufügt, oder ich füge das Element hinzu Zur Liste hinzufügen oder wie Sie bereits gesagt haben, wird der Artikel zur Liste hinzugefügt - alle sind korrekt. Die Frage ist also, wer der Schauspieler ist und warum? Ein Gegenstand ist auch ein Schauspieler und er möchte einer Gruppe (Liste) beitreten, nicht die Gruppe möchte diesen Gegenstand haben ;-) Der Gegenstand handelt aktiv, um in einer Gruppe zu sein.Parent
Eigenschaft hat. Zwar ist Englisch kann nicht Muttersprache , aber soweit ich weiß , will nicht passiv , es sei denn ich will mich oder er will , dass mir etwas tun ; -]List<T>
(zumindest der in gefundeneSystem.Collections.Generic
) aktualisiert überhaupt keineParent
Eigenschaft. Wie könnte es sein, wenn es generisch sein soll? Es liegt normalerweise in der Verantwortung des Containers, seinen Inhalt hinzuzufügen.