2

Notes:

  1. Besides the logical differences, I am also interested to know about the technical differences as reflected in C# specifically (hence, it is not a question for Programmers).
  2. This question is a bit similar, but it asks about methods while I am asking about classes, so it is not a duplicate.

Circle and Rectangular are shapes. Both have a perimeter and an area, but different implementations for calculating them. I can see three different way to implement such a logic, and I am not sure what is the difference between these approaches.

Using dynamic polymorphism:

class Shape
{
    public virtual double Perimeter() { /* logic */ }
    public virtual double Area() { /* logic */ }
}

class Rectangular : Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

class Circle : Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

Using an abstract class:

abstract class Shape
{
    public abstract double Perimeter() {}
    public abstract double Area() {}
}

class Rectangular : Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

class Circle: Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

Using an interface:

interface IShape
{
    double Perimeter();
    double Area();
}

class Rectangular : IShape
{
    public double Perimeter() { /* logic */ }
    public double Area() { /* logic */ }
}

class Circle: IShape
{
    public double Perimeter() { /* logic */ }
    public double Area() { /* logic */ }
}

In the title, I mentioned that I am interested in an answer from the perspective of OOP. I want to understand the theoretical differences between the approaches, based on the OOP paradigm, and from that - understand the technical differences. For example, I know that interfaces methods cannot have an implementation while virtual methods can, but I do not understand why it consists with the OOP paradigm.

Please answer with all the theoretical differences and for each difference, the derived technical difference in C#.

Many thanks.


Edit: @AdrianoRepetti and @Biscuits say that my question is vague. My English is not great, so I will try to explain myself as clearly as I can.

I showed three different ways of doing the same thing. But is it really the same thing? What are the differences between them from a program architecture POV? I mean, when I design the program, why should I choose one over the other? What are the essential differences, and how those differences are expressed in the syntax of C#? I hope my question is clearer now.

If someone who speaks good English think he/she understand my question and can edit it to be clearer and grammarly correct, I will be grateful.

Thanks a lot!

Community
  • 1
  • 1
Michael Haddad
  • 4,085
  • 7
  • 42
  • 82
  • From my point of view you should explain what you mean with _" I want to understand the theoretical differences between the approaches, based on the OOP paradigm"_. It sounds tremendously vague to me. Difference between - for example - interfaces and base abstract classes has been widely discussed (and it's not just about method implementation). Can you narrow little bit? – Adriano Repetti May 19 '16 at 06:55
  • @AdrianoRepetti Yes, of course. I will edit my question. – Michael Haddad May 19 '16 at 06:56
  • Your question is vague. Why is it that you think a question about OOP is not for programmers? – Biscuits May 19 '16 at 06:58
  • 2
    @Biscuits The Programmers stackexchange site, not programmers as in human developers (yet another reason its name should finally be changed) – Alexander Derck May 19 '16 at 07:00
  • @AdrianoRepetti - please review my edit... Thanks! – Michael Haddad May 19 '16 at 07:14
  • Even after edit don't see how is it different from lots of other similar questions of that type. – Evk May 19 '16 at 07:46
  • @Evk - I did not find a similar question. If you find one, please link to it. – Michael Haddad May 19 '16 at 07:51
  • 1
    For example this one - http://stackoverflow.com/q/56867/5311735. – Evk May 19 '16 at 07:54
  • @Evk - My question is more general. I ask also about the relationship between interfaces-polymorphism and abstract class-polymorphism... – Michael Haddad May 19 '16 at 08:07

4 Answers4

2

Your three approaches are completely different then you can't really compare them. Let's see why.

Non Abstract Base Class

You have a base class but it's not abstract and it provides a default implementation. First question you should ask yourself is if this makes sense.

Shape shape = new Shape();
Console.WriteLine($"Area of this shape is {shape.Area}");
  • Does a generic Shape exist?
  • Can you calculate Area of an unknown shape?

If answer is yes then you can work with it...

Non Polymorphic Abstract Base Class

This makes little sense to me, you do not override base class methods in base class, think about this:

Circle circle = new Circle();
Shape shape = circle;

// When invoking with a Shape instance you will call
// be class method, when with a Circle instance you will call derived
// class method!!!
Debug.Assert(shape.Area != circle.Area);

Also note that again base class provides an implementation for both Area and Perimeter, does it make sense for a generic shape?

Interface

IMO interface (or an abstract base class) is what makes sense in this case. In simple words you're just asking for an interface to access shape objects, you do not provide a generic (non existing?) implementation and called method are what you probably expect.

Disadvantages? Imagine you later add a IEnumerable<Point> GetPoints() method to IShape, for simplicity imagine that this method may return null when implementation cannot return the polyline to draw this shape. Now your code is broken until you update all classes that implements IShape. If you deployed IShape as part of a library then you're also introducing a breaking change (I don't repeat here much about this, it has been widely discussed elsewhere).

One note (even if I understand that this is just a fictional example): does Area exist for all shapes? What if you introduce an open polyline? In this case interfaces make more sense:

interface IShape
{
    double Perimiter { get; }
}

interface IClosedFigure : IShape
{
    double Area { get; }
}

interface IHasPoints
{
    IEnumerable<Point> GetPoints();
}

sealed class Circle : IClosedFigure { /* ... */ }
sealed class Polyline : IShape, IHasPoints { /* ... */ }

Are there alternatives? Yes, something middle-way between your second and third approaches...

Abstract Base Class

Pick your second approach and mark base class methods as abstract. This will effectively produce the same IL code as your third approach (without the disadvantage of an interface):

abstract Shape
{
    public abstract double Area { get; }
}

sealed Circle : Shape
{
    public override double Area 
    {
        get { /* logic */ } 
    }
}

Note that you can still add a method and introduce a breaking-change if you mark it as abstract. Disadvantages? You're now forcing a base class and C# is single inheritance.

Note that you don't need to have only abstract methods/properties, you may provide an implementation for some/all of them. In this case this approach is more similar to your first proposed one.

Conclusions

Second approach is pretty corner-case and should be seldom used in well-designed architectures. First one may have sense if some circumstances and if you can provide a sensible default implementation (for example you may calculate area using a slow method, through a GetPoints(), and provide faster calculations for known shapes). If base class should not be instantiated then you should also mark it as abstract (making this approach more near to fourth one).

Third approach and the other proposed one (base abstract class) are somehow equivalent but pretty different in intent. For a discussion about this topic you may read Interface vs Base class as starting point (there is much more material about this).

Community
  • 1
  • 1
Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
1

As others have said, it's hard to compute the perimeter or area of an unknown Shape, so you cannot have default implementations for them in the base class.

The choice then is between an abstract class and an interface. The main differences are:

  • An interface permits "multiple inheritance"; a C# class can implement multiple interfaces but inherit from only one class. Making the base class an interface gives the programmer more freedom (something completely unrelated could also be a Shape). In your case though my gut feeling is that Shape is a "main concept", which establishes the root class of an inheritance hierarchy.

  • An abstract base class can have state and (default) implementations for common functionality, which an interface cannot. Conceivably a Shape could have a position or a transformation matrix (for rotation, translation, scaling) attached to it. There is a design choice here: Instead of having data for a point and a matrix defined in a base class one can declare methods for manipulating them in an interface. The latter is often preferred because there is less "lock in" with obsolete data and implementations once the class hierarchy evolves.

Sometimes a library combines both worlds, providing "adapters" for interfaces which provide empty or standard implementations for many functions in order to reduce boiler plate coding for the user. This is very common in the Java standard libraries. People who have no need for multiple inheritance just derive from the adapter and have less coding to do.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • Upvoted. I like the mix solution (interface + default implementation), It gives the best freedom and usability. – Oscar Vicente Perez May 19 '16 at 09:43
  • @OscarVicentePerez I like it too. It works best though when the methods aren't closely coupled. An event handler is a good example: You may only want to do something special for a keyboard event; ignoring mouse events (the default) is completely fine. By contrast, any default for computing properties or values like perimeter, area, center of mass for Shapes is likely wrong for any specialization, so providing a Shape adapter which returns 0.0 everywhere is a source of errors. – Peter - Reinstate Monica May 19 '16 at 11:51
  • That's right, for the problem of Shapes I'll go for Interface, as you need the methods implementation by contract and you can't have a default behaviour in any of the methods he show. – Oscar Vicente Perez May 19 '16 at 12:10
0

In a nutshell:

Using dynamic polymorphism: Use this, when the base class has an implementation that can be changed by child classes. This class can be fully functional without the need of child classes.

Using an abstract class: If you want to enforce child classes to implement functionality. The base class isn't complete enough to create it as an standalone object.

Using an interface: If you want to use a contract for this class. Use it to make classes 'compatible' so the functionality is (partial) available for other classes who 'understand' the interface.


In your case, I would create an interface IShape, because (base)Shape itself can't implement the Area calculation method (so it shouldn't be dynamic polymorphism), also you don't need any state information from the shape it self.(typically abstract class) All calculation variables are implemented in the (child)class itself. Like for Rectangular (width/height) and for the Circle the radius etc. The advantage for an interface is, you can implement multiple interfaces.

If the Shape is a control, the story changes...

Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57
0

As a rule of thumb, use the least powerfull tool for the job:

  • Interfaces only declare methods that the inheriting classes have to implement, but don't provide functionality on their own.

  • Abstract base classes can in addition have fully implemented member functions or data members, but can not be instantiated themselves.

  • A normal base class can be instantiated on its own own, so only use this if it makes sense to have it as an object of that type.

And if there isn't a is-a relationship between base/interface and dereived class, don't use any of them at all.

One additional thing to consider ist that you can implement multiple interfaces, but only inherit from one abstract base class, but that is rarely an issue.

So in your case, use an interface if you don't want to share implementation details among the different Shapes and an abstract base class, if you do. Creating a generic Shape object doesn't make much sense so don't make Shape a regular base class.

MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • " if there isn't a is-a relationship between base/interface and dereived class, don't use any of them at all": I always thought that public inheritance is *the* way to express an is-a relationship? – Peter - Reinstate Monica May 19 '16 at 08:01
  • @Peter: Correct, and you should only introduce that relationship on the language level if it reflects a natural relationship of the the semantic/behavioral level and it satisfies the Liskov substitution principle. E.g. java's stack class is unfortunately derived from vector, although there is (in my opinion) no natural is-a relation ship between stack and vector. – MikeMB May 19 '16 at 09:48
  • Oh. If I hadn't copied verbatim I would swear any oath that you added the negation ("isn't") after my comment ;-). I somehow missed it. Yes, public inheritance indeed reflects an is-a relationship. Sorry for the confusion. – Peter - Reinstate Monica May 19 '16 at 11:15