Class relationships: architectural advice needed

Let's say we have some classes that have a relationship between them. Geometry or math libraries have Vector3, Matrix4, Plane3..etc as an example. There are many intersection testing methods in between. For example, a test intersection between Plane3 and Vector3; if Vector3 (as a point) is on a plane, out of plane back and out of plane for front ... etc.

Thus, the intersection testing method can be written for both Vector3 and Plane3 classes. But this causes slight complication and repetition of hardcoding. So is there any advice for this situation.

  • Implement the method in one class, which is more important to have it, specifically on Plane3, because it is more used for intersections, its purpose is to test for intersections and things like that.
  • Implement both classes.
  • Implement utilities in the class as a static method.
  • Other

The first case may be good, but sometimes the situation is not clear, as they say. And the second requires more repetitive code for many classes with a large number of methods that lead to Cartesian code multiplication. The third might be good, but sometimes it can't access private or protected methods of the classes (unless implemented as a friend) and I don't know at all how to classify the utility classes and it will be more difficult for the user to know where the method / she is looking for ... So is there any approbation for this situation?

Edit: more detailed examples.

class A {}
class B {}
class C {}

      

Firstly:

bool A::IsIntersecting(const B& b) const;
bool A::IsIntersecting(const C& c) const;
bool B::IsIntersecting(const C& c) const;

      

Secondly:

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);

      

+3


source to share


3 answers


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 difficult to understand when consumers are trying to figure out where the function is intersectionTest

for a certain pair of geometric objects. for example, is it in Line

or Point

? What about testing if Line

crosses a Plane

? I find it tempting to choose an arbitrary rule, for example if the higher-sized objects contain features that match the lower-sized, but then you run into the same problem when you test objects with the same dimensions as a cube and a circle.

Implement on both classes.

      



This is too much repetitive code. However, you can get away with making a basic implementation for 1D, 2D, 3D, etc., which can do most of the work. This will obviously use inheritance, and you said you didn't want to do that.

Implement in a utility class as a static method.

      

The reason this has no obvious solution is that the intersection of two (or more that doesn't seem like what you want to consider) of these objects doesn't actually belong to either of them. This refers to the "space" in which they are located. So my suggestion was to think less of one object crossing another, and instead build a solution based on their position in "space" , no matter space, 2d, etc. At the end of the day, whether you implement this as simple or static functions, the base class or the container that holds your objects is up to you.

+1


source


If you have a function that works with multiple classes, you might consider making it a standalone function. Remember: not every function needs to be associated with a class (unless you are using Java or C #). Thus, you can have something like:

bool intersects(const T1 &a, const T2 &b);

      

The next thing to consider is what relationships are possible and make sense (for example, it doesn't make sense to ask if a vector intersects a matrix). This will show you what combinations of classes to use with this method.

Then we will consider the equivalences within the method. If A op B == B op A

, you can write:



inline bool intersects(const Vector3 &a, const Plane3 &b)
{
    return intersects(b, a);
}

      

It is used to implement the relational operators - you can implement !=

in terms of ==

and >

, >=

, <=

in terms of <

.

This also applies to compositions (eg calls intersects

in coordinates x

and y

object Point2D

).

If you are writing a library, you should write the most common / useful combinations first and target the complex set (where they make sense). If you are writing an application, you should focus on providing what you need.

+1


source


I would suggest offering a isIntersecting

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 just make them friends if you need to.

0


source







All Articles