Je développe un modèle objet qui comporte de nombreuses classes parent / enfant. Chaque objet enfant a une référence à son objet parent. Je peux penser (et avoir essayé) plusieurs façons d’initialiser la référence parente, mais je trouve des inconvénients importants pour chaque approche. Compte tenu des approches décrites ci-dessous, quelle est la meilleure ... ou encore, la meilleure solution?
Je ne vais pas m'assurer que le code ci-dessous est compilé, essayez donc de voir mon intention si le code n'est pas correct du point de vue de la syntaxe.
Notez que certains de mes constructeurs de classes enfants prennent des paramètres (autres que parent) même si je n'en affiche pas toujours.
L'appelant est responsable de la définition du parent et de l'ajout au même parent.
class Child { public Child(Parent parent) {Parent=parent;} public Parent Parent {get; private set;} } class Parent { // singleton child public Child Child {get; set;} //children private List<Child> _children = new List<Child>(); public List<Child> Children { get {return _children;} } }
Inconvénient: la définition du parent est un processus en deux étapes pour le consommateur.
var child = new Child(parent); parent.Children.Add(child);
Inconvénient: risque d'erreur. L'appelant peut ajouter un enfant à un parent différent de celui utilisé pour l'initialiser.
var child = new Child(parent1); parent2.Children.Add(child);
Parent vérifie que l'appelant ajoute l'enfant au parent pour lequel il a été initialisé.
class Child { public Child(Parent parent) {Parent = parent;} public Parent Parent {get; private set;} } class Parent { // singleton child private Child _child; public Child Child { get {return _child;} set { if (value.Parent != this) throw new Exception(); _child=value; } } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public void AddChild(Child child) { if (child.Parent != this) throw new Exception(); _children.Add(child); } }
Inconvénient: l' appelant a toujours un processus en deux étapes pour définir le parent.
Inconvénient: vérification de l'exécution - réduit les performances et ajoute du code à chaque ajout / configurateur.
Le parent définit la référence parent de l'enfant (à lui-même) lorsque l'enfant est ajouté / attribué à un parent. Le parent parent est interne.
class Child { public Parent Parent {get; internal set;} } class Parent { // singleton child private Child _child; public Child Child { get {return _child;} set { value.Parent = this; _child = value; } } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public void AddChild(Child child) { child.Parent = this; _children.Add(child); } }
Inconvénient: l'enfant est créé sans référence parent. Parfois, l’initialisation / validation nécessite le parent, ce qui signifie qu’une initialisation / validation doit être effectuée dans le parent parent de l’enfant. Le code peut devenir compliqué. Il serait tellement plus facile d'implémenter l'enfant s'il avait toujours sa référence parent.
Le parent expose des méthodes d'ajout d'usine afin qu'un enfant ait toujours une référence parent. Le ctor de l'enfant est interne. Le parent parent est privé.
class Child { internal Child(Parent parent, init-params) {Parent = parent;} public Parent Parent {get; private set;} } class Parent { // singleton child public Child Child {get; private set;} public void CreateChild(init-params) { var child = new Child(this, init-params); Child = value; } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public Child AddChild(init-params) { var child = new Child(this, init-params); _children.Add(child); return child; } }
Inconvénient: impossible d'utiliser une syntaxe d'initialisation telle que
new Child(){prop = value}
. Au lieu de faire:var c = parent.AddChild(); c.prop = value;
Inconvénient: Doivent dupliquer les paramètres du constructeur enfant dans les méthodes add-factory.
Inconvénient: impossible d'utiliser un créateur de propriété pour un enfant singleton. Il semble boiteux que j'ai besoin d'une méthode pour définir la valeur mais fournir un accès en lecture via un getter de propriété. C'est déséquilibré.
Child s'ajoute au parent référencé dans son constructeur. Le ctor de l'enfant est public. Aucun public n'ajoute l'accès du parent.
//singleton class Child{ public Child(ParentWithChild parent) { Parent = parent; Parent.Child = this; } public ParentWithChild Parent {get; private set;} } class ParentWithChild { public Child Child {get; internal set;} } //children class Child { public Child(ParentWithChildren parent) { Parent = parent; Parent._children.Add(this); } public ParentWithChildren Parent {get; private set;} } class ParentWithChildren { internal List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } }
Inconvénient: la syntaxe d'appel n'est pas géniale. Normalement, on appelle une
add
méthode sur le parent au lieu de simplement créer un objet comme ceci:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
Et définit une propriété plutôt que de simplement créer un objet comme celui-ci:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Je viens d'apprendre que SE n'autorise pas certaines questions subjectives et qu'il s'agit clairement d'une question subjective. Mais, peut-être que c'est une bonne question subjective.