I have a Person class and two inherited classes called Parent and Child. A Parent can have n Child(s) and a Child can have n Parent(s).
What is the best way in OOD to create a reference between a Parent and a Child.
Should I create a List in each class referencing the connected Parent/Child or is there a better way?
Great question. Pure many-to-many relationships are actually quite rare, and it usually helps to introduce an intermediate object to model the relationship itself. This will prove invaluable if (when!) use cases emerge which require the capture of properties regarding the relationship (e.g. whether the child/parent relationship is natural, surrogate, adoptive, etc).
So, in addition to the Person, Parent and Child entities which you've already identified, let's introduce an object called ParentChildRelationship.
An instance of ParentChildRelationship will have a reference to exactly one Parent and One Child, and both the Parent and Child classes will hold a collection of these entities.
It's a good idea to then identify the use cases you have for working with these entities, and add appropriate helper methods to maintain the inter-object references.
In the example below I've just chosen to add a public AddChild method to the parent.
public abstract class Person
{
}
public class Parent : Person
{
private HashSet<ParentChildRelationship> _children =
new HashSet<ParentChildRelationship>();
public virtual IEnumerable<ParentChildRelationship> Children
{
get { return this._children; }
}
public virtual void AddChild(Child child, RelationshipKind relationshipKind)
{
var relationship = new ParentChildRelationship()
{
Parent = this,
Child = child,
RelationshipKind = relationshipKind
};
this._children.Add(relationship);
child.AddParent(relationship);
}
}
public class Child : Person
{
private HashSet<ParentChildRelationship> _parents =
new HashSet<ParentChildRelationship>();
public virtual IEnumerable<ParentChildRelationship> Parents
{
get { return this._parents; }
}
internal virtual void AddParent(ParentChildRelationship relationship)
{
this._parents.Add(relationship);
}
}
public class ParentChildRelationship
{
public virtual Parent Parent { get; protected internal set; }
public virtual Child Child { get; protected internal set; }
public virtual RelationshipKind RelationshipKind { get; set; }
}
public enum RelationshipKind
{
Unknown,
Natural,
Adoptive,
Surrogate,
StepParent
}
public class Person
{
Person Parent { get;set; }
IList<Person> Children { get;set; }
}
Parent can be null when you do not know the parent.
Children can be null or empty when you have no children.
Since each child is a Person, it can have a Parent or its own children.
This design, by itself, is fine until you provide more detailed use case scenarios about how it will be used or persisted.
If you can limit the direction of the association to go only one way, you will save yourself a lot of trouble (but this is not always possible).
One-way relationship:
public class Parent : Person
{
public IEnumerable<Person> Children { get; }
}
If you want to have the association going the other direction as well, you can do so too:
public class Child : Person
{
public Parent Parent { get; }
}
However, now you have a circular reference that you need to maintain, and while it's possible, it's not particularly productive.
You can often keep the association as a one-way relationship by letting the children raise events instead of explicitly referencing their parent.
As JohnIdol pointed out, a child someway down the line might become a parent. In other words DON'T make Parent and Child sub-classes of Person.
class Person
{
readonly List<Person> _children = new List<Person>(),
_parents = new List<Person>();
public IEnumerable<Person> Children
{
get { return _children.AsReadOnly(); }
}
public IEnumerable<Person> Parents
{
get { return _parents.AsReadOnly(); }
}
public void AddChild(Person child)
{
_children.Add(child);
child._parents.Add(this);
}
public void AddParent(Person parent)
{
_parents.Add(parent);
parent._children.Add(this);
}
/* And so on... */
}
I would imagine that a child can also be a parent down the line (if he gets lucky ... or unlucky, depending on points of view) so I would go with something like:
IPerson
{
string Name {get; set;}
string LastName {get; set;}
// whatever else - such as sizeOfShoe, dob, etc
}
IHaveParents
{
// might wanna limit this to a fixed size
List<IPerson> Parents {get; set;}
}
IHaveChildren
{
List<IPerson> Children {get; set;}
}
IHaveSpouse
{
IPerson Spouse {get; set;}
}
public class DudeWithParentsAndChildren : IPerson, IHaveParents, IHaveChildren, IHaveSpouse
{
public void AskMoneyToParents(){throw new Exception("Implement me!");}
public void SlapChildren(){}
private void CheatOnSpouse(){}
// some other stuff that such a dude can do i.e. GoBowling
}
And you could easily extend this any way you like when new requirements come along (trust me they will).
Update:
So in your case if you only want a Child to have Parents and the other way around you'd do something like:
public class Child : IPerson, IHaveParents
{
public void AskMoneyToParents(){throw new Exception("Implement me!");}
}
public class Parent : IPerson, IHaveChildren, IHaveSpouse
{
public void SlapChildren(){}
private void CheatOnSpouse(){}
// some other stuff that such a dude can do i.e. GoBowling
}
This way if you want to have an IHaveFriends interface you can (which basically forces the implementer to expose a list of IPersons as a property named Friends). If you don't need it don't do it, but the fact that you can easily do it just adding an interface an everything else stays the same means you've got a pretty decent extensible model (not necessarily the best, you know what I mean).
Related
I'm trying to utilize automapper for some conversions between two classes.
I have four classes - two parent classes where both have a child collection of the remaining two.
public class ParentA
{
public IEnumerable<ChildA> Children{get;set;}
}
public class ChildA
{
public ParentA Parent{get;set;
}
public class ParentB
{
public IEnumerable<ChildA> Children{get;set;}
}
public class ChildB
{
public ParentA Parent{get;set;
}
I need to ensure that the Parent property is set for both collections before mapping occurs. For the source object I do the following:
CreateMap<ParentA, ParentB>()
.BeforeMap((a, b) =>
{
foreach (var entry in a.Entries)
entry.Parent = a;
})
However, I do not know how to do the same for the destination object. I need to check the parent due to some conditional mapping and value resolution.
Given the following over-simplified code:
public class Child
{
public virtual Parent Parent { get; set; }
}
public class Parent
{
public List<Child> Children { get; set; }
}
In my consumer code, I'd have:
parent.Children.Add(child);
This does not set the child.Parent, until I call db.SaveContext();
I see situations in which this is a problem, e.g. chaining a couple of operations on the same object before saving.
My question is, should I be doing this instead:
class Child
{
public virtual Parent Parent { get; set; }
public void SetParent(Parent parent) {
if (this.Parent != null) { this.Parent.Children.Remove(this); }
parent.Children.Add(this);
this.Parent = parent;
}
}
Please note the code snippet is just for illustrative purpose.
Generally my question is, should I handle the relational fix up myself, instead of relying on EF.
For simplicity, I would try to avoid the kind of chained operations that would require you to have to do this, but I don't think there is anything wrong with doing it if you need it.
A suggestion: Do not directly expose the Children collection in your Parent class, make its setter method private or protected, and add methods to add and remove Children elements in the Parent class. This is the way to ensure that the logic to set Child parent is always executed whenever the collection changes.
public class Parent
{
public List<Child> Children { get; protected set; }
public void AddChild(Child child)
{
Children.Add(child);
child.Parent = this;
}
public void RemoveChild(Child child)
{
Children.Remove(child);
child.Parent = null;
}
}
I have create a generic Node class which will be used to build a tree object. I want to inherit the attributes of this class in another. The job class represents a SQL job which is included in a chain (tree) of jobs. The following code is giving me an error and I am not sure why.
public class Node<T>
{
public int id { get; set; }
public Node<T> parent { get; set; }
public List<Node<T>> Children = new List<Node<T>>();
public bool isRoot
{
get { return parent == null; }
}
public static Node<T> createTree(List<Node<T>> nodes)
{
if (nodes.Count() == 0)
return new Node<T>();
//Build parent / Child relationships
}
}
public class Job : Node<Job>
{
public string name {get; set;}
public Job(String name)
{
this.name = name;
}
}
List<Job> joblist = JobDict.Select(j => new Job(j.Key)).ToList();
Node<Job>.createTree(joblist);
I am Unable to call createTree with the List of Jobs. I realize changing it from List < Job > to List < Node< Job > > works but why am I unable to do the former? I figured because I am inheriting the node class, a List of Jobs would in fact be equivalent to a List of Node. I am sorry if this is a very basic question but I just began with generics and inheritance and am having a hard time grasping it entirely.
The problem is that List<Node<Job>> and List<Job> are not co-variant.
If you're using .NET 4 you can do this.
Node<Job>.createTree((IEnumerable<Node<Job>>)joblist);
or, you can modify the creeatetree method definition as follows.
public static Node<T> createTree(IList nodes)
{
if (nodes.Count == 0)
return new Node<T>();
//Build parent / Child relationships
}
I realize changing it from List<Job> to List<Node<Job>> works
but why am I unable to do the former?
Because List<Job> does not inherit List<Node<Job>> even if Job inherits Node<Job>. In other words, A inherits B does not mean List<A> inherits List<B>.
You may need to cast each Job object to Node<Job> first:
var jobNodeList = joblist.Select(j => (Node<Job>)j).ToList();
Node<Job>.createTree(jobNodeList);
This isn't a direct answer to the question, but it might give you some ideas on how to make your code simpler.
It's often a good idea to keep code simple. The simplest tree structure you can make is this:
public class Tree<T> : List<Tree<T>> { }
In your case, you would extend it slightly as:
public class Node<T> : List<Node<T>>
{
public int id { get; set; }
public Node<T> parent { get; set; }
public bool isRoot
{
get { return parent == null; }
}
}
Effectively you get all of the Children properties for free.
I just try to redesign my Silverlight-4 App and tried a bit around with generics.
Simply speaking, I have a tree which can contain 2 types of nodes. As a base class, I created a class that does all the "organization", like having a list of children, a parent, a method to add a child and so on:
public abstract class BaseNode<T> : INotifyPropertyChanged where T: BaseNode<T>
{
protected ObservableCollection<T> _children;
...
}
Second, I add a class that inherits from BaseNode and is the basis for all my treenodes:
public class ServiceNodeBase<T> : BaseNode<ServiceNodeBase<T>> where T : ServiceNodeBase<T>
{
public string Description { get; set; }
...
}
And finally, as I can have two different kinds of nodes, I create a class for each kind, i.e.:
public class ServiceNodeComponent<T> : ServiceNodeBase<ServiceNodeComponent<T>> where T : ServiceNodeComponent<T>
{
public HashSet<Attributes> Attributes { get; set; }
...
}
In the ServiceNodeComponent, I need a method, that scans the tree i.e. to get all the child nodes, that are of the Type ServiceNodeComponent. When parsing the tree, I need to use the parent-type of ServiceNodeComponent (ServiceNodeBase), because the child nodes can also be of the other type.
Now, I do not know how to instantiate the ServiceNodeBase-Variable.
public HashSet<ServiceNodeComponent<T>> GetAllChildComponents()
{
// declaring the container for the found Components is no problem
HashSet<ServiceNodeComponent<T>> resultList = new HashSet<ServiceNodeComponent<T>>();
// but now: how to declare the container for the ServiceBaseNodes?
HashSet<ServiceNodeBase<???>> workingList = new HashSet<ServiceNodeBase<???>>();
Any ideas, how I would implement this?
Thanks in advance,
Frank
The problem is the constraint. It would work if you change it to
public class ServiceNodeComponent<T> : ServiceNodeBase<ServiceNodeComponent<T>>
where T : ServiceNodeBase<T> {
public HashSet<ServiceNodeComponent<T>> GetAllChildComponents() {
// ...
HashSet<ServiceNodeBase<T>> workingList = new HashSet<ServiceNodeBase<T>>();
// ...
}
}
I've a question regarding enforcing a business rule via a specification pattern. Consider the following example:
public class Parent
{
private ICollection<Child> children;
public ReadOnlyCollection Children { get; }
public void AddChild(Child child)
{
child.Parent = this;
children.Add(child);
}
}
public class Child
{
internal Parent Parent
{
get;
set;
}
public DateTime ValidFrom;
public DateTime ValidTo;
public Child()
{
}
}
The business rule should enforce that there cannot be a child in the collection which validity period intersects with another.
For that I would like to implement a specification that is then be used to throw an exception if an invalid child is added AND as well can be used to check whether the rule will be violated BEFORE adding the child.
Like:
public class ChildValiditySpecification
{
bool IsSatisfiedBy(Child child)
{
return child.Parent.Children.Where(<validityIntersectsCondition here>).Count > 0;
}
}
But in this example the child accesses the parent. And to me that doesnt seem that correct. That parent might not exist when the child has not been added to the parent yet. How would you implement it?
public class Parent {
private List<Child> children;
public ICollection<Child> Children {
get { return children.AsReadOnly(); }
}
public void AddChild(Child child) {
if (!child.IsSatisfiedBy(this)) throw new Exception();
child.Parent = this;
children.Add(child);
}
}
public class Child {
internal Parent Parent { get; set; }
public DateTime ValidFrom;
public DateTime ValidTo;
public bool IsSatisfiedBy(Parent parent) { // can also be used before calling parent.AddChild
return parent.Children.All(c => !Overlaps(c));
}
bool Overlaps(Child c) {
return ValidFrom <= c.ValidTo && c.ValidFrom <= ValidTo;
}
}
UPDATE:
But of course, the real power of the specification pattern is when you can plug in and combine different rules. You can have an interface like this (possibly with a better name):
public interface ISpecification {
bool IsSatisfiedBy(Parent parent, Child candidate);
}
And then use it like this on Parent:
public class Parent {
List<Child> children = new List<Child>();
ISpecification childValiditySpec;
public Parent(ISpecification childValiditySpec) {
this.childValiditySpec = childValiditySpec;
}
public ICollection<Child> Children {
get { return children.AsReadOnly(); }
}
public bool IsSatisfiedBy(Child child) {
return childValiditySpec.IsSatisfiedBy(this, child);
}
public void AddChild(Child child) {
if (!IsSatisfiedBy(child)) throw new Exception();
child.Parent = this;
children.Add(child);
}
}
Child would be simple:
public class Child {
internal Parent Parent { get; set; }
public DateTime ValidFrom;
public DateTime ValidTo;
}
And you could implement multiple specifications, or composite specifications. This is the one from your example:
public class NonOverlappingChildSpec : ISpecification {
public bool IsSatisfiedBy(Parent parent, Child candidate) {
return parent.Children.All(child => !Overlaps(child, candidate));
}
bool Overlaps(Child c1, Child c2) {
return c1.ValidFrom <= c2.ValidTo && c2.ValidFrom <= c1.ValidTo;
}
}
Note that it makes more sense to make Child's public data immutable (only set through the constructor) so that no instance can have its data changed in a way that would invalidate a Parent.
Also, consider encapsulating the date range in a specialized abstraction.
I think the Parent should probably do the validation. So in the parent you might have a canBeParentOf(Child) method. This method would also be called at the top of your AddChild method--then the addChild method throws an exception if canBeParentOf fails, but canBeParentOf itself does not throw an exception.
Now, if you want to use "Validator" classes to implement canBeParentOf, that would be fantastic. You might have a method like validator.validateRelationship(Parent, Child). Then any parent could hold a collection of validators so that there could be multiple conditions preventing a parent/child relationship. canBeParentOf would just iterate over the validators calling each one for the child being added--as in validator.canBeParentOf(this, child);--any false would cause canBeParentOf to return a false.
If the conditions for validating are always the same for every possible parent/child, then they can either be coded directly into canBeParentOf, or the validators collection can be static.
An aside: The back-link from child to parent should probably be changed so that it can only be set once (a second call to the set throws an exception). This will A) Prevent your child from getting into an invalid state after it's been added and B) detect an attempt to add it to two different parents. In other words: Make your objects as close to immutable as possible. (Unless changing it to different parents is possible). Adding a child to multiple parents is obviously not possible (from your data model)
Would you not have an If statement to check that a parent was not null and if so return false?
You are trying to guard against Child being in an invalid state. Either
use the builder pattern to create fully populated Parent types so that everything you expose to the consumer is always in a valid state
remove the reference to the Parent completely
have Parent create all instances of Child so this can never occur
The latter case might look (something) like this (in Java):
public class DateRangeHolder {
private final NavigableSet<DateRange> ranges = new TreeSet<DateRange>();
public void add(Date from, Date to) {
DateRange range = new DateRange(this, from, to);
if (ranges.contains(range)) throw new IllegalArgumentException();
DateRange lower = ranges.lower(range);
validate(range, lower);
validate(range, ranges.higher(lower == null ? range : lower));
ranges.add(range);
}
private void validate(DateRange range, DateRange against) {
if (against != null && range.intersects(against)) {
throw new IllegalArgumentException();
}
}
public static class DateRange implements Comparable<DateRange> {
// implementation elided
}
}