私は多くの異なる親/子クラスを持つオブジェクトモデルを開発しています。各子オブジェクトには、その親オブジェクトへの参照があります。親参照を初期化するいくつかの方法を考えることができます(そして試してみました)が、各アプローチには重大な欠点があります。以下で説明するアプローチのうち、どれが最良であるか、またはそれよりも優れている場合を考えます。
以下のコードがコンパイルされることを確認しませんので、コードが構文的に正しくない場合は、私の意図を確認してください。
常に表示するわけではありませんが、一部の子クラスコンストラクターは(親以外の)パラメーターを取ることに注意してください。
呼び出し元は、親を設定し、同じ親に追加する責任があります。
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;} } }
欠点:親の設定は、消費者にとって2段階のプロセスです。
var child = new Child(parent); parent.Children.Add(child);
欠点:エラーが発生しやすい。呼び出し元は、子を初期化するために使用されたものとは異なる親に子を追加できます。
var child = new Child(parent1); parent2.Children.Add(child);
親は、呼び出し元が初期化された親に子を追加することを確認します。
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); } }
欠点:発信者には、親を設定するための2段階のプロセスがまだあります。
欠点:実行時チェック–パフォーマンスが低下し、すべての追加/設定者にコードが追加されます。
親は、子が親に追加/割り当てられるときに、子の親参照を(それ自体に)設定します。親セッターは内部です。
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); } }
欠点:子は親参照なしで作成されます。初期化/検証には親が必要な場合があります。つまり、子の親セッターで初期化/検証を実行する必要があります。コードは複雑になる可能性があります。常に親参照がある場合、子を実装するのは非常に簡単です。
親はファクトリのaddメソッドを公開し、子が常に親参照を持つようにします。子ctorは内部です。親セッターはプライベートです。
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; } }
欠点:などの初期化構文を使用できません
new Child(){prop = value}
。代わりに:var c = parent.AddChild(); c.prop = value;
欠点: add-factoryメソッドで子コンストラクターのパラメーターを複製する必要があります。
欠点:シングルトンの子にはプロパティセッターを使用できません。値を設定するメソッドが必要であるが、プロパティゲッターを介して読み取りアクセスを提供することは不十分なようです。それは不均衡です。
子は、コンストラクタで参照される親に自分自身を追加します。子俳優は公開されています。親からのパブリック追加アクセスはありません。
//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;} } }
欠点:構文を呼び出すのは良くありません。通常
add
、次のようなオブジェクトを作成する代わりに、親でメソッドを呼び出します。var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
そして、次のようなオブジェクトを作成するだけでなく、プロパティを設定します。
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
SEは主観的な質問を許可していないことを知りましたが、これは明らかに主観的な質問です。しかし、多分それは良い主観的な質問です。