There are two approaches to solving such a precedent:
1. Implement Multiple Dispatch :
Start by creating a Curve interface and add two overloaded intersect versions to this interface, making them part of the contract. Then, if the intersection(Curve c) method in each of the subclasses delegates the call to the corresponding overloaded form. (aka visitor template )
interface class Curve { public Point[] intersection(Curve c); public Point[] intersection(Line l); public Point[] intersection(Arc c); } class Line extends Curve { public Point[] intersection(Curve c) { return c.intersection(this); } @Override public Point[] intersection(Line l) { System.out.println("line interesection with line"); return new Point[0]; } @Override public Point[] intersection(Arc c) { System.out.println("line intersection with arc"); return new Point[0]; } } class Arc extends Curve { public Point[] intersection(Curve c) { return c.intersection(this); } @Override public Point[] intersection(Line l) { System.out.println("arc interesection with line"); return new Point[0]; } @Override public Point[] intersection(Arc c) { System.out.println("arc interesection with arc"); return new Point[0]; } }
Then you can call your intersection method in the intersection class without any explicit tricks:
public class Intersection { public static boolean intersect(ArrayList<Curve> list1, ArrayList<Curve> list2) { for (Curve i : list1) { for (Curve j : list2) { if (i.intersection(j).length > 0) return true; } } return false; } public static void main(String[] args) { Curve line1 = new Line(); Curve arc1 = new Arc(); Curve line2 = new Line(); Curve arc2 = new Arc(); ArrayList<Curve> list1 = new ArrayList<>(); ArrayList<Curve> list2 = new ArrayList<>(); list1.add(line1); list1.add(arc1); list2.add(line2); list2.add(arc2); Intersection.intersect(list1, list2); } }
Additional features . Check out this alternative approach to visitor pattern implementation.
2. Make a line and a curve to stick to the same interface (contract) :
If Line and Arc adhere to the Curve interface, your code will no longer need overloaded versions of the intersect method. If we say that a Line is Curve and Arc also Curve , both of these classes should have the same interface as Curve (by interface I mean the list of supported operations). If these classes do not have the same interface as Curve , the problem is here. The methods presented in Curve should be the only methods that should be required by the Line and Arc classes.
There are several strategies to eliminate the need for subclasses to have methods that are not present in the superclass:
- If a subclass requires additional inputs compared to a superclass, provide these inputs through the constructor, rather than creating separate methods that work on these inputs.
- If a subclass requires additional behavior that is not supported by the superclass, support this behavior using composition (read the strategy template) rather than adding methods to support additional behavior.
Once you eliminate the need for specialized methods in a subclass that are not present in the superclass, your code automatically eliminates the need for instanceof or type of validation. This is consistent with the Liskov substitution principle .
Cking source share