views:

73

answers:

4

Here's the simplest form of my question:

IApple requires, among other things, property Flavor IToffeeApple also requires property Flavor

The problem is, I want IToffeeApple to implement IApple (public interface IToffeeApple : IApple), but they both have the same property requirement. This becomes a problem when, for 1 purpose I need a Collection of Sweets (only IToffeeApple) where IToffeeApple can also be recognised as IApple.

Is it ok to use the "new" keyword in my interface, so that any implementers have 2 Flavor properties?

Have I explained myself poorly? :\

edit: I have. The actual context is geometry:

  • ILine requires a Start and End point IPoint.
  • ICurve requires a Start and End point IControlPoint.

ICurve is just extra functionality on top of ILine, yet it means I want to return the Start and End as IControlPoint rather than IPoint, so I either implement both and have a return of both IControPoint and IPoint of Start and End, or I just ignore IPoint/ILine and throw DRY out the window.

A: 

No, because IToffeeApple is only inheriting from IApple (it isn't implementing anything, it's an interface) and there's no conflict; just leave the Flavour property out so it inherits it from IApple.

This doesn't becomes a property with a collection that only has IToffeeApple, because there won't be any conflicting cases there either.

It could only really become a problem if something implemented both IToffeeApple, and either something else where flavour had a different type, or IQuark where flavour has a different meaning. In this case the class implementing them should implement on or both of the properties explicitly.

Jon Hanna
+1  A: 

No, you don’t need the new keyword to inherit a property from an interface in another interface:

public interface IApple
{
    Flavor Flavor { get; }
}

public interface IToffeeApple : IApple
{
    ICollection<Sweet> Sweets { get; }
}

public class MyToffeeApple : IToffeeApple
{
    public Flavor Flavor { get { return Flavors.ToffeeFlavor; } }
    public ICollection<Sweet> Sweets { get { return new Sweet[0]; } }
}

Works just fine. If this doesn’t answer your question, please edit the question to include detail that explains in what way the above is insufficient.

Timwi
+1  A: 

Try something along the lines of:

interface IFlavor;
interface ISweets: IFlavor;
interface IApple: IFlavor;
interface IToffeeApple: IApple, ISweets;

IEnumerable<ISweets> can hold the IToffeeApple, but not IApple.

When there seems to be a need to 'replace' an inherited property for the inheritance make sense I generally look for one of two things. Either the inheritance is forced (a car and an apple both have a color, but are not often thought of as polymorphic), or the inheritance is deeper than it seemed at first glance.

If I understand the example well enough, deeper inheritance fits well enough.


To fit your new example:

public interface IPoint
{}

public interface IControlPoint : IPoint
{
    // added functionality of IControlPoint
}

public interface ILine<TPoint>
    where TPoint : IPoint
{
    TPoint Start { get; set; }
    TPoint End { get; set; }
}

public interface ICurve<TPoint> : ILine<TPoint>
    where TPoint : IPoint
{
    // added functionality of ICurve
}

I am making the assumption that IControlPoint implements IPoint, but it seemed reasonable.

Basically, generics takes care of ICurve needing an IControlPoint to work with, while ILine needs an IPoint.

David Culp
+2  A: 

This is an elaboration of David Culp's answer. It's quite long, so I'm not posting as a comment.

While the generic interface IList<TPoint> looks like a good solution, I would recommend to elaborate the semantics a bit further. Also, it's advisory to consider introducing both generic and non-generic versions of some interface (like IEnumerable plus IEnumerable<T>).

In your clarified example you should introduce a term describing a finite entity that has a starting and ending point. Generally a curve is not a line but a line is a curve. However, since your curve uses ControlPoints to describe its start and end, while the line uses plain Points instead, in your specific case a line is not a curve. Instead, both line and curve are something like a shape.

// this interface will denote any shape that has a starting and ending point
public interface IShape
{
    IPoint Start { get; set; }
    IPoint End { get; set; }
}

// this interface will allow for specialization of staring end ending points' type
public interface IShape<TPoint> : IShape
{
    // note 'new' will be required here most probably, I didn't compile it
    TPoint Start { get; set; }
    TPoint End { get; set; }
}

// a line will be a shape descibed by a pair of points; for the sake of 
public interface ILine : IShape<Point> { }

// a curve will be a shape described by a pair of control points
public interface ICurve : IShape<ControlPoint> { }

Then, the actual implementations of ILine and ICurve can use explicit implementations of the Start and End properties coming from the non-generic IShape interface. This will favor strongly-typed access to the delimiting points, while preserving the ability to work with them as with plain IShapes.

 public class Curve : ICurve 
 {
     public ControlPoint Start { get; set; }
     public ControlPoint End { get; set; }
     IShape.Start 
     {
         get { return Start; }
         set 
         {
              if (!(value is ControlPoint)) ... some error handling ...; 
              Start = (ControlPoint)value; 
         }
     }
     IShape.End { ... similarly as IShape.Start ... }
 }

 public class Line : ILine { ... similarly as Curve ... }

Note that in respect to the possible error scenario shown above an improvement could be limiting IShapes properties to getters only. Or, the setter passing the value into the strongly-typed version of Start or End could do some kind of conversion of a point into a control point. The right solution is domain-specific of course.

Ondrej Tucny
Great! So at some point it *is* appropriate to explicitly state the implementation. One of my hurdles was that for anything that was an extension of IPoint (like IControlPoint), i'd be stuck with Start and End returning IPoint rather than the specific implementation (with extra methods) like IControlPoint, but by making the implementation explicit, it's returning exactly what I want while still conforming to the interface (ie. Start/End is still IPoint, but my caller isnt locked into just getting the IPoint implementation).
George R
By combining the generic and non-generic approach, and favoring one over the another by choosing which one to implement explicitly, you can gain quite easily an extra bit of flexibility. Generics are very helpful in keeping the code strongly-typed, but in certain scenarios a non-generic alternative comes in handy for the sake of uniformity, reusability, and readability of certain pieces of code code.
Ondrej Tucny
+1 -- I pointed in a direction. You construct a road. Nicely done.
David Culp