How should I abstract a collection of objects owned by another object?
Question
In a system for managing vocational training, I have a CourseBase
abstract class, which I decided on using in favour of an ICourse
interface because I'd prefer to avoid duplicating implementation code for all classes derived from the hypothetical, base Course
entity. Each course has a list if subjects, with any subject defined by a SubjectBase
abstract class. So, I have e.g.
public abstract class CourseBase : BaseObject
{
public IEnumerable<SubjectBase> Subjects
{
get { return new List<SubjectBase>(); }
}
}
public abstract class SubjectBase
{
public string Name { get; set; }
public string Description { get; set; }
public int ValidityPeriod { get; set; }
}
Now I want to add a concrete class, LocalCourse
, which contains a collection of LocalCourseSubject
objects, but because I'm not using an interface for CourseBase
, I lose out on covariance, and I need to hide the abstract base's Subjects
property with my new:
public class LocalCourse: CourseBase
{
public IEnumerable<LocalCourseSubject> Subjects
{
get { throw new NotImplementedException(); }
}
}
I'm sure I'm missing something very obvious here from an OO point of view, but the only solutions I can see are:
- Completely omit Subjects from the abstract base, and only add a specifically typed collection property to derived classes.
- Implement an interface such as
ISubjectCollectionOwner
in the abstract base as well as concrete classes.
Please excuse my dimness here, it's been a while since I've had the pleasure of encountering a design issue like this.
Solution
Can't you just do this:
public abstract class CourseBase<T> where T : SubjectBase
{
public virtual IEnumerable<T> Subjects
{
get { return new List<T>(); }
}
}
public abstract class SubjectBase
{
public string Name { get; set; }
public string Description { get; set; }
public int ValidityPeriod { get; set; }
}
public class LocalCourse : CourseBase<LocalCourseSubject>
{
public override IEnumerable<LocalCourseSubject> Subjects
{
get { throw new NotImplementedException(); }
}
}
I think that would accomplish your short term goal, at any rate, assuming that the general pattern is that each CourseBase inheritor will have a collection of the same type of SubjectBase inheritor. But, if that is the case, this seems like a parallel inheritance hierarchy, which can sometimes be a code smell (not saying that it necessarily is -- I don't know all the details of the domain you're modeling).
OTHER TIPS
Why just not introduce a generic interface to abstract a Course? Sorry if I missed something obvious
public interface ICourse<TSubject>
{
IEnumerable<TSubject> Subjects { get; }
}
public abstract class CourseBase<TSubject>
: BaseObject,
ICourse<TSubject>
{
public IEnumerable<TSubject> Subjects
{
get { return new List<TSubject>(); }
}
}
public class LocalCourse
: CourseBase<LocalCourseSubject>
{
}
If Subject is a vital part of Course entity you should keep it within both ICourse and CourseBase as well, otherwise I would suggects abstracting it by ISubjectAware interface