4

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.

ProfK
  • 49,207
  • 121
  • 399
  • 775
  • Which version of C# and .NET are you using? It makes a difference in terms of generic variance. Do you really need all the abstraction here, by the way? What different types of courses and subjects will you have? – Jon Skeet Nov 06 '11 at 09:30
  • 1
    isn't the point of the SubjectBase so that you can treat all subjects the same using polymorphism. can't you just get rid of your Subjects override and use the CourseBase collection of subjects instead? – Cosmin Onea Nov 06 '11 at 09:35
  • @Jon, v4 on both counts. My 'spec' here is to cater for 'all kinds of things', so I'm just gambling on getting enough abstraction in early to support wide extensions to the basic entities. – ProfK Nov 06 '11 at 11:53
  • @Cosmin, I think you may have something there, that I have just been overlooking. Thanks, I'll check it out. – ProfK Nov 06 '11 at 11:54

2 Answers2

2

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

sll
  • 61,540
  • 22
  • 104
  • 156
  • I think this is a bad idea. @Cosman above has got the right idea. Using generics like this will break polymorphism and you won't be able to handle the collection of subjects in a generic way. – xanadont Nov 06 '11 at 16:13
  • I'm not sure I got it right - `you won't be able to handle the collection of subjects in a generic way`, could you give an example please? – sll Nov 06 '11 at 17:46
  • 1
    I would've been able to give you an example, if this were pre-.Net-4.0. But I just now got schooled on covariance and doing something like "IEnumerable objs = new List();" is possible. This is interesting and may change my future object model designs. Sorry for the noise. – xanadont Nov 07 '11 at 03:20
1

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).

Erik Dietrich
  • 6,080
  • 6
  • 26
  • 37