Object Orientated Design Parent / Child Relationship
This is a general best practice question about creating parent / child relationships with objects.
Let's say I have Wheel and Car objects and I want to Add a Wheel object to car object
public class Car{
private List<Wheel> wheels = new List<Wheel>();
void AddWheel ( Wheel WheelToAdd)
{
wheels.Add(WheelToAdd)
//Some Other logic relating to adding wheels here
}
}
}
So far so good. But what if I want to have a Car property of my wheel to say which parent car it relates to. Something like this
public class Wheel {
private Car parentCar;
public Car
{
get
{
return parentCar
}
}
}
When adding the wheel to the car, at which point would you set the parent property of the Wheel? You could set it in the Car.AddWheel method, but t开发者_开发问答hen the Car Property of the Wheel object would have to be Read/Write and then you could set it outside of the AddWheel method, creating an inconsistency.
Any thoughts, many thanks in advance
A better design methodology, (Domain Driven Design) specifies that you should first decide what the domain model requirements are for these entities... Not all entities need to be independantly accessible, and if Wheel
falls into this category, every instance of it will always be a child member of a Car
object and you don't need to put a Parent property on it... Car
becomes what is referred to as a root entity, and the only way to access a Wheel
is through a Car
object.
Even when the Wheel
object needs to be independantly accessible, the domain model requirements should tell you what the usage patterns require. Will any Wheel
ever be passed around as a separate object, without it's parent ? In those cases is the Car
parent relevant? If the identity of the parent Car
is relevant to some piece of functionality, why aren't you simply passing the complete composite Car
object to that method or module? Cases where a contained composite object (like a Wheel
) must be passed on it's own, but the identity of the parent (the object it is part of) is needed and/or relevant, are in fact not a common scenario, and approaching your design using the above type of analysis can save you from adding unnessary code to you system.
Bidirectional relationships tend to be very difficult to implement correctly. The prevailing advice here should be "don't do that, most likely you don't actually need it, and it will do you more harm than good."
If, after much careful consideration, you decide a bidirectional relationship is warranted, you can make the setter of the Car
property internal
, which doesn't fully protect against rogues setting unwanted values but it does limit the surface area significantly.
You might want to consider having a setter that only sets Wheel.parentCar
if that setting is null, but only if you're able to assume that your first setting will be valid and are thus able to disregard any other attempts.
Edit: but yes, that would be the proper place to add the Car
object. You could also do a check such that you create (for example) Wheel.validateCar(Car carInQuestion)
, where it enforces that the parentCar
property is only set where the current Wheel
object exists in Car
. This implies that you would have a public method for searching Car.wheels
for membership based on a particular instance of a wheel. But that's only if you really feel the need to be strict.
It makes sense to me for the Car property of the Wheel object to be writable because there's nothing to stop you moving a Wheel from one ToyotaCamry (subclass of Car) to another. Though you might want to have a Drivable property on the original car to check that List.Count is still 4 before allowing Car.Drive() to be called.
Do you create Wheels with Cars? if so set the property at that time. Otherwise set it when Wheel is attached to Car.
Question: Can the Wheel instance be assigned to a different Car instance later in time?
1) If yes, the expose a public method to set the parentCar. You could make it a fluent method, for ease of code:
public class Wheel
{
private Car parentCar;
public ParentCar
{
get
{
return parentCar;
}
}
public void SetParentCar(Car _parentCar)
{
parentCar = _parentCar;
return this;
}
}
Where you assign add the wheel, make the assignment:
void AddWheel ( Wheel WheelToAdd)
{
wheels.Add(WheelToAdd.SetParentCar(this));
//Some Other logic relating to adding wheels here
}
2) If not, just set the parent in the constructor.
public class Wheel
{
private Car parentCar;
public ParentCar
{
get
{
return parentCar;
}
}
public Wheel(Car _parentCar)
{
parentCar = _parentCar;
}
}
I'm going to go with "Don't". A wheel isn't just a property of a Car
. A wheel could be used on a Trailer
or a Cart
or a FerrisWheel
(ok, maybe that's a bit of a stretch). The point is, by making the car which uses the wheel a property of the wheel itself, you couple your wheel's design to being used on a car, and at that point, you lose any reusability for the class.
In this case, it seems like there's a lot to lose and little to gain by letting the wheel know what it's actually used on.
Take a look at Is it bad practice for a child object to have a pointer to its parent?
May be a little left of centre but conceptually a wheel is a "part" of a car. Did you consider using partial classes? A passenger is a "part" of a queue. I find the use of partial classes and well defined interfaces is very useful in some scenarios. You can abstract out aspects of whole object into interfaces and implement each interface as a partial class. The result is a complete object that is able to be abstracted by its partial interface to meet an array of contractual scenarios.
精彩评论