Reputation: 587
Let say we have some classes which have relations between them. AS an example geometry or math libraries have Vector3, Matrix4, Plane3..etc. There are lots of intersection test methods between them. For example testing intersection between Plane3 and Vector3; if Vector3(as point) on plane, out of plane back and out of plane front...etc.
So the intersection test method can be written on both Vector3 and Plane3 class. But this causes a litlle complication and repeating of hard coding. So is there any advice for this situation.
The first case can be good but sometimes the situation is not clear as it says. And the second requires more repeated code specialy for lots of classes with lots of methods causes cartesian multiplication of code increasing. Third one can be good but sometimes it cannot access private or protected methods of classes (if not implemented as friend) and I generaly don't know how to categorize utility classes and it will be more diffucult for user to know where is the method he/she is looking for. So is there any aproaches for this situation?
Edit: More detailed examples.
class A {}
class B {}
class C {}
First:
bool A::IsIntersecting(const B& b) const;
bool A::IsIntersecting(const C& c) const;
bool B::IsIntersecting(const C& c) const;
Second:
bool A::IsIntersecting(const B& b) const;
bool A::IsIntersecting(const C& c) const;
bool B::IsIntersecting(const A& a) const;
bool B::IsIntersecting(const C& c) const;
bool C::IsIntersecting(const A& a) const;
bool C::IsIntersecting(const B& b) const;
Third:
bool IntersectUtility::IsIntersecting(const A &a, const B &b);
bool IntersectUtility::IsIntersecting(const A &a, const C &c);
bool IntersectUtility::IsIntersecting(const B &b, const C &c);
Upvotes: 3
Views: 71
Reputation: 3273
Implement the method on one class which has more meaning to has it, specically on Plane3 because it is used more for intersections, its aim is, intersection test and this kind of things.
This solution "smells" to me, for several reasons. The biggest problem I see is that it would be very confusing when consumers are trying to figure out where the intersectionTest
function for a particular pair of geometric objects is located. eg is it in Line
or Point
? What about testing if the Line
intersects a Plane
? I can see a temptation to pick an arbitrary rule, like higher-dimensional objects contain the functions corresponding to the lower-dimensional, but then you run into the same problem when you're testing objects of the same dimensions.
Implement on both classes.
This is too much duplicate code. However, you might be able to get away with creating a base implementation for 1D, 2D, 3D, etc that is able to do the majority of the work. This would obviously utilize inheritance, and you said that you didn't want to do that.
Implement in a utility class as a static method.
The reason that this doesn't have an obvious solution is that the intersection of the two (or more, which it doesn't seem like you want to consider) of these objects doesn't really belong to either of them. It belongs to the "space" they're in. So my suggestion would be to think less about one object intersecting another and instead build a solution based on their position in "space", whether that's 1D-space, 2d, etc. At the end of the day, whether you implement this as bare or static functions, a base class, or a container that holds your objects is up to you.
Upvotes: 1
Reputation: 15334
I would suggest making isIntersecting
a set of non-member functions in the same namespace as A
, B
and C
:
namespace X {
class A { };
class B { };
class C { };
bool isIntersecting(const A&, const B&);
bool isIntersecting(const A&, const C&);
bool isIntersecting(const B&, const C&);
}
And only make them friend if you have to.
Upvotes: 0
Reputation: 8145
If you have a function that operates across multiple classes, you could consider making it a free-standing function. Remember: not every function needs to be associated with a class (unless you are using Java or C#). Thus, you could have something like:
bool intersects(const T1 &a, const T2 &b);
The next thing to consider is what relations are possible and make sense (e.g. it does not make sense to ask if a vector intersects a matrix). This will tell you what combinations of classes should be used with the method.
Next, consider equivalences within the method. If A op B == B op A
, then you can write:
inline bool intersects(const Vector3 &a, const Plane3 &b)
{
return intersects(b, a);
}
This is used when implementing the relational operators -- you can implement !=
in terms of ==
and >
, >=
, <=
in terms of <
.
This also applies for compositions (e.g. calling intersects
on the x
and y
coordinates of a Point2D
object).
If you are writing a library, you should write the most common/useful combinations first and aim for a comprehensive set (where they make sense). If you are writing an application, you should focus on providing what is needed.
Upvotes: 2