I have a tricky modelling problem, on which I’d like to have another point of view.
I’m dealing with a model that is treated as a whole by some engines. It looks like that (names are not what we are using):
public class Container{
// Some other specific properties and methods, ctor etc
public IList<Group> Groups {get; private set;}
// For convenience, we need that
public IList<Element> Elements {get; private set;}
}
public class Group{
// Specific stuff here
public IList<Element> Elements{get; private set;}
public Container Container{get; set;}
}
public class Element{
public Group Group {get; set;}
}
All along the model, we have those double references, because the model is treated as a whole by engines.
Now, the tricky part: I need to create 2 specific models, sharing this organization.
Currently, I have a model with three classes: Container – Group – Element.
I need to create specific models sharing the same structure, but with specific classes all along the model.
- BaseContainer, BaseGroup, BaseElement (parent)
- ContainerFoo – GroupFoo – ElementFoo (first child)
- ContainerBar – GroupBar – ElementBar (second child)
And, to be complete, the most important thing is that I need strongly typed collections. For example, I need a list of GroupFoo in ContainerFoo, not a list of the parent type.
I explored 2 ways of achieving this : generics and creating new properties.
For example:
public class BaseContainer<TGroup, TElement>{
public IList<TGroup> Groups {get; private set;}
public IList<TElement> Elements{get; private set;}
}
public class BaseGroup<TContainer, TElement>{
public TContainer Container {get; set;}
public IList<TElement> Elements {get; private set;}
}
public class BaseElement<TGroup>{
public TGroup Group{get; set;}
}
public class ContainerFoo: BaseContainer<GroupFoo, ElementFoo>{
// Specific stuff here
}
public class GroupFoo: BaseGroup<ContainerFoo, ElementFoo>{
}
public class ElementFoo: BaseElement<ContainerFoo>{
}
This solution works in this case but:
- List of types in container can be very long, because the container is actually the entry point of the whole model (simplified here)
- We can’t actually use it with protobuf-net, used for serialization and deserialization.
Second solution:
public abstract class BaseContainer{
public abstract IList<BaseGroup> Groups {get;}
public abstract IList<BaseElement> Elements{get;}
}
public abstract class BaseGroup{
public abstract BaseContainer Container {get; set;}
}
public abstract class BaseElement{
public abstract BaseGroup Group{get; set;}
}
public ContainerFoo : BaseContainer{
public override IList<BaseGroup> Groups {
get{
// We are using .Net 4, and we can do that.
return (IList<BaseGroup>)this.GroupsFoo;
}
}
public IList<GroupFoo> GroupsFoo{ get; private set;}
// Same thing for elements
}
// You see the point, I don't want to create other classes here.
I think you can obviously see what I don’t like about this second solution!
Any more ideas ?
Instead of using
abstract/overrideyou can use thenewmodifier on the properties of the derived classes:Alternatively, is there any real benefit in using inheritence here? Would you be better off defining a generic
IContainer<T>interface and doing away with the base class?