Estou desenvolvendo um modelo de objeto que possui várias classes pai / filho diferentes. Cada objeto filho tem uma referência ao seu objeto pai. Posso pensar em (e tentei) várias maneiras de inicializar a referência pai, mas encontro desvantagens significativas em cada abordagem. Dadas as abordagens descritas abaixo, qual é a melhor ... ou o que é ainda melhor.
Não vou garantir que o código abaixo seja compilado; portanto, tente ver minha intenção se o código não estiver sintaticamente correto.
Observe que alguns dos meus construtores de classe filho usam parâmetros (que não sejam pai), mesmo que nem sempre eu os mostre.
O chamador é responsável por definir o pai e adicionar ao mesmo pai.
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;} } }
Desvantagem: definir pai é um processo de duas etapas para o consumidor.
var child = new Child(parent); parent.Children.Add(child);
Desvantagem: propenso a erros. O chamador pode adicionar um filho a um pai diferente do que foi usado para inicializar o filho.
var child = new Child(parent1); parent2.Children.Add(child);
O pai verifica se o chamador adiciona o filho ao pai para o qual foi inicializado.
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); } }
Desvantagem: o chamador ainda tem um processo de duas etapas para definir o pai.
Desvantagem: verificação do tempo de execução - reduz o desempenho e adiciona código a cada add / setter.
O pai define a referência do pai da criança (para si mesmo) quando o filho é adicionado / atribuído a um pai. O setter pai é interno.
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); } }
Desvantagem: o filho é criado sem uma referência pai. Às vezes, a inicialização / validação requer o pai / mãe, o que significa que alguma inicialização / validação deve ser realizada no pai / mãe da criança. O código pode ficar complicado. Seria muito mais fácil implementar o filho se ele sempre tivesse sua referência principal.
Pai expõe métodos de adição de fábrica para que um filho sempre tenha uma referência pai. O filho é interno. O setter dos pais é privado.
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; } }
Desvantagem: não é possível usar sintaxe de inicialização como
new Child(){prop = value}
. Em vez disso, tem que fazer:var c = parent.AddChild(); c.prop = value;
Desvantagem: É necessário duplicar os parâmetros do construtor filho nos métodos de adição de fábrica.
Desvantagem: Não é possível usar um configurador de propriedades para um filho único. Parece lame que eu preciso de um método para definir o valor, mas fornecer acesso de leitura através de um getter de propriedade. É desequilibrado.
Filho se adiciona ao pai referenciado em seu construtor. O child child é público. Nenhum acesso público de adição do pai.
//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;} } }
Desvantagem: chamar a sintaxe não é ótimo. Normalmente, chama-se um
add
método no pai, em vez de apenas criar um objeto como este:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
E define uma propriedade em vez de apenas criar um objeto como este:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Acabei de aprender que a SE não permite algumas perguntas subjetivas e, claramente, essa é uma pergunta subjetiva. Mas, talvez seja uma boa pergunta subjetiva.