Reputation: 632
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
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
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
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
Reputation: 16708
Try this out:
interface IMove { move(); }
abstract class Shape { }
class Circle : Shape, IMove { }
class Square : Shape { }
class Triangle : Shape, IMove { }
Upvotes: 3
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
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:
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