Reputation: 33
I have a question regarding OOP composition.
Let's say that a mother has 0 or plus children, and a child has one and only one biologic mother.
To illustrate it, I did the following :
public class Mother : ObservableObject
{
// [...]
ObservableCollection<Child> Children {get; set;}
}
public class Child : ObservableObject
{
public Child(Mother mother)
{
this.Mother = mother;
// Adding the child to the mother's children collection
mother.Children.Add(this);
}
public Mother Mother {get; set;}
}
but I wonder if it's okay to automatically add the child to the mother's collection, or if I should go with the following :
Mother mother = new Mother();
Child child = new Child(mother);
mother.Children.Add(child);
Thanks :)
Upvotes: 3
Views: 168
Reputation: 39
Suppose you have a class name building. This class building has a function called BuildRooms(). There is another class Room having functions to make rooms. You create objects of class Room as r1, r2,r3 etc. Now this building has 3 rooms. We can open doors and close doors methods inside Room class. Building class is composed of Rooms. It means this building has 3 rooms. Write code in any language you prefer. This is composition. Building has rooms so it is Has-A relationship.
class building{
void make_rooms()
{
room r1=new room(), r2=new room();
r1.open();
r2.close();
}
}
class room{
void open()
{
}
void close()
{
}
}
Upvotes: 0
Reputation: 218798
I think the modeling is a little off. A Mother
and a Child
are semantically related to one another, but they're instances of the same object. They are both a Person
.
The creation of a Person
is an operation performed by a Person
. So a Person
shouldn't even have a public constructor, but rather a factory method which takes care of this logic. Something like this:
public class Person : ObservableObject
{
private Person()
{
Children = new ObservableCollection<Person>();
}
public Person Mother { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
public Person Procreate()
{
var child = new Person();
child.Mother = this;
this.Children.Add(child);
return child;
}
}
This modeling is still a bit limited, for example we're only talking about asexual reproduction here. So we're not effectively modeling humans yet. Perhaps we need to add a father?
public class Person : ObservableObject
{
private Person()
{
Children = new ObservableCollection<Person>();
}
public Person Mother { get; private set; }
public Person Father { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
public Person Procreate(Person father)
{
var child = new Person();
child.Mother = this;
child.Father = father;
this.Children.Add(child);
father.Children.Add(child);
return child;
}
}
We'll want to add some checking for nulls and whatnot of course. Now we've also discovered that we need to specify genders. (While family structures may vary considerably, the act of creating a person is pretty well established.) So we can keep adding features like that. At some point we may indeed subclass these, but those subclasses will likely end up being mostly semantic pass-through objects with hard-coded default values for this Person
superclass.
But just for fun, let's try adding genders...
public class Person : ObservableObject
{
private Person(Sex gender, Person mother, Person father)
{
// TODO: Check for null mother and father
this.Gender = gender;
this.Mother = mother;
this.Father = father;
Children = new ObservableCollection<Person>();
}
public Sex Gender { get; private set; }
public Person Mother { get; private set; }
public Person Father { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
public Person Procreate(Person father)
{
// TODO: Check for null father, confirm gender of father
var child = new Person(PickRandomGender(), this, father);
this.Children.Add(child);
father.Children.Add(child);
return child;
}
private Sex PickRandomGender() { /.../ }
public enum Sex
{
Female,
Male
}
}
Ok, that was fun. Cleaned up a little bit by moving some logic to the constructor as well. But now there's another problem... fathers can procreate. Which sounds kind of painful. Now it looks like we're ready to subclass:
public class Person : ObservableObject
{
protected Person(Sex gender, Person mother, Person father)
{
// TODO: Check for null mother and father
this.Gender = gender;
this.Mother = mother;
this.Father = father;
Children = new ObservableCollection<Person>();
}
public Sex Gender { get; private set; }
public Person Mother { get; private set; }
public Person Father { get; private set; }
public ObservableCollection<Person> Children { get; private set; }
protected Sex PickRandomGender() { /.../ }
public enum Sex
{
Female,
Male
}
}
public class Woman : Person
{
// TODO: Override Gender with a hard-coded value
public Person Procreate(Person father)
{
// TODO: Check for null father, confirm gender of father
var child = new Person(PickRandomGender(), this, father);
this.Children.Add(child);
father.Children.Add(child);
return child;
}
}
(Should we subclass a Man
as well? It semantically seems cleaner, but are there any operations or attributes specific to men that aren't shared by women? Perhaps, but our models aren't that detailed yet.)
Looking back, the classes of Mother
and Child
seem kind of limited and short-sighted at this point. A woman isn't necessarily a mother, and all people are children. As you can imagine, there are plenty of features to add to this system. But following the same general process of building out the domain like this should accommodate that.
Upvotes: 2
Reputation: 35696
I'd prefer,
public class Mother : ObservableObject
{
// ...
public Child GiveBirth()
{
var newBorn = new Child(this);
this.Children.Add(newBorn);
return newBorn;
}
// ...
}
Upvotes: 5