Search code examples
c#.netoopooad

How should I abstract a collection of objects owned by another object?


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:

  1. Completely omit Subjects from the abstract base, and only add a specifically typed collection property to derived classes.
  2. 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).