sok
sok

Reputation: 632

Extend interface to an abstract class

I have an interface (move) that should move some shapes.

interface Move { move(); }
abstract class Shape : Move

class Circle : Shape
class Square : Shape
class Triangle : Shape

My doubt is, I must have an interface which moves Shapes but only Circle and Triangle should be able to be moved, so how do I "remove" the interface from Square? Should I remove the interface from Shape and add it manually on Circle and Triangle? I'm kinda confused with this. Hope someone can help me out.

Upvotes: 20

Views: 4514

Answers (6)

Mike Perrenoud
Mike Perrenoud

Reputation: 67898

You should setup your classes like this:

interface IMovable { move(); } 
abstract class Shape : { } 

class Circle : Shape, IMovable { } 
class Square : Shape { } 
class Triangle : Shape, IMovable { } 

If not every shape can be moved then Shape must not implement the interface. Also note I renamed your interface to IMovable, it's not a huge deal but it's more accepted and a better naming convention.

Upvotes: 34

Steven McGrath
Steven McGrath

Reputation: 1727

The best answer will depend on what the use case and environment for these classes will be. As part of a team developing an app or framework, adopting the design patterns used by that team is preferable to seeking a 'perfect' solution, since it will make it easier for others to adopt and maintain your code.

How you expect these classes to be used and extended is also important. Would you expect 'Square' to need to be movable in the future? Is the movability of a Shape always static, or might it be more useful as a dynamic attribute? Does Move() have any value for classes that are not Shapes? If movability may be useful as a dynamic attribute, consider this:

public abstract class Shape
{
     public bool isMovable()
     {
         return false;
     }

     public virtual void Move()
     { 
         if (!isMovable() {
             throw new NotSupportedException();
         } else {
             throw new BadSubclassException();
         }
     }
}

Your subclasses can then override isMovable to provide either static or dynamic behavior, and can be further modified or subclassed over time, so long as your documentation makes clear that isMoveable should always precede a call to Move. The default behavior should be based on the expectations of others you expect to use your code, based on how they've implemented related design patterns.

A good example of the challenge of making these decisions can be found by looking at the history of how mutability of collection classes has evolved in different frameworks. There have been designs where the mutable classes (sets, arrays, dictionaries, etc.)have been the base class, with immutability implemented in subclasses, as well as the reverse. There are valid arguments for both approaches, as well as a dynamic approach, but the most important factor for the user of a framework is consistency, because what is correct is really a matter of what is easiest to use, providing safety and performance are not compromised.

Upvotes: 2

Matías Fidemraizer
Matías Fidemraizer

Reputation: 64943

Other option could be just implement IMove.Move method in the Shape class and throw a NotSupportedException by default.

public abstract class Shape : IMove 
{
     public virtual void Move()
     { 
         throw new NotSupportedException();
     }
}

So at the end of the day, "any shape could be movable" but "a movable shape should provide its own implementation of how to move it".

Finally, let's imagine there're a bunch of shapes that are moved in the same way. You'd create a DefaultMovableShape abstract class deriving Shape, which overrides Shape.Move virtual method.

public abstract class DefaultMovableShape 
{
     public override void Move()
     {
           // Do stuff
     }
}

Upvotes: 2

Furqan Safdar
Furqan Safdar

Reputation: 16708

Try this out:

interface IMove { move(); }
abstract class Shape { }

class Circle : Shape, IMove { }
class Square : Shape { }
class Triangle : Shape, IMove { }

Upvotes: 3

Oded
Oded

Reputation: 499072

You can't remove an interface from an inheritance tree.

What you model appears to need two abstract classes - Shape and MovableShape.

interface IMove { move(); } 
abstract class Shape : {} 
abstract class MovableShape : IMove, Shape {} 

class Circle : MovableShape{}
class Square : Shape{}
class Triangle : MovableShape{}

Upvotes: 27

Achim
Achim

Reputation: 15712

You should make yourself more familiar with the ideas behind interfaces, classes and OO in general. What you are trying to tell is the following:

  • Every shape can be moved.
  • A Square is a shape.
  • But a Square cannot be moved.

Obivously that makes not sense. So you have to adjust your class design. Either every shape can be moved, that Shape (and Square) should implement Move, or not every shape can be moved, then Shape should not implement Move.

Upvotes: 3

Related Questions