Reputation: 75
I am sure this must have been asked before but I cannot seem to find a similar example. I understand well polymorphism and method overloading, but here is a seemingly simple scenario with a solution that escapes me:
let's say I have a base class with several derived classes. I will use shapes for this example
base Shape
derived Circle extends Shape
derived LineSeg extends Shape
etc.
Now, shape has a method called intersect(other) that tests against another shape to see if they intersect. With polymorphism it is easy to see how Circle, LineSeg, etc., can implement their own "intersect" methods, and, with method overloading, I can easily implement all needed combinations. e.g.,
Circle.intersect(LineSeg)
Circle.intersect(Circle)
LineSeg.intersect(Circle)
etc.
so far so good.
the problem is, if I keep a central list of shapes, I want to do this:
for some shape s
Foreach shape in Shapes
if (s.intersect(shape)) - do something
Currently, I am not sure how this is possible, since the method overloading selects the "intersect" method to match the base type Shape, and not the appropriate parameter type. How can I make this without an if-else chain checking the types and down-casting?
BTW, I am using Java, but I am not sure that the language is entirely relevant since it seems to be a basic design question. Seems so simple, what am I missing?
Thanks!
Solved below (Thanks!), see details there. Basically, by having a callback in the derived classes that then calls the appropriate method (a visitor pattern?), you can use the "this" keyword to invoke the proper intersect method, as it has the proper type needed.
Upvotes: 5
Views: 231
Reputation: 8350
Your shape class must be an abstract class, meaning it does not get to be instantiated, only the derived classes circle and lineseg have instances. The intersect method should be virtual in shape so when you loop over all shapes, the intersect method of each shape is called
public abstract class Shape {
boolean intersect(Shape s);
}
public class Circle extends Shape {
boolean intersect(Shape s) {
...
if( s instanceOf Circle ) {
.... // Circle intersects cicrcle
} else if( s instanceOf Lineseg ) {
.... // Circle intersects Lneseg
} else {
throw RuntimeException("Unrecognized shape");
}
}
}
public class Lineseg extends Shape {
boolean intersect(Shape s) {
...
if( s instanceOf Circle ) {
.... // Lineseg intersects circle
} else if( s instanceOf Lineseg ) {
.... // Lineseg intersects lineseg
} else {
throw RuntimeException("Unrecognized shape");
}
}
}
Upvotes: 0
Reputation: 1245
My first thought was the visitor pattern, pretty much give every Shape two methods, one I'll call intersect(Shape)
and one doIntersect()
method per Shape type.
It would look about like this:
interface Shape {
public abstract Intersection intersect(Shape other);
public abstract Intersection doIntersect(Circle circle);
public abstract Intersection doIntersect(LineSeg line);
}
class LineSeg implements Shape {
@Override
public Intersection intersect(Shape other) {
return other.doIntersect(this);
}
Intersection doIntersect(Circle circle) {
// Code to intersect with Circle
}
Intersection doIntersect(LineSeg other) {
// Code to intersect with another Lineseg
}
}
class Circle implements Shape {
@Override
public Intersection intersect(Shape other) {
return other.doIntersect(this);
}
public Intersection doIntersect(Circle other) {
// Code to intersect with another Circle
}
public Intersection doIntersect(LineSeg segment) {
// Code to intersect with LineSeg
}
}
You might want the doIntersect methods to be package private or chose different names than these though.
Upvotes: 4