Home > Software design >  avoiding code duplication in different classes where the function does the same in C
avoiding code duplication in different classes where the function does the same in C

Time:12-24

I'm kinda new to OOP so this question feels a bit weird but I want to know what I should do in this case

Say I have a Tup4 class which just holds 4 doubles, and two classes Point4 and Vec4 that extend Tup4. Now, checking for equality in Tup4 is just comparing whether all 4 doubles in each tuple are (approximately) equal. This holds in both classes extending it. However, it makes no sense to define an equality function in Tup4, because then I would be able to check for equality between a Point and a Vector, which doesn't make much sense. So I can't define a virtual equals method in Tup4, so what can I do? The code is exactly the same in both cases, the only difference is the type of the function. So I want to know if I can avoid having two methods

bool equals(Point4 p);
bool equals(Vec4 v);

Where they both do the same but are defined in different classes

CodePudding user response:

Looks like you already accepted an answer. But here's what I was going to propose without going down the template route:

Define an equality method in your Tup4 class, but leave it protected:

class Tup4
{
public:
    double a, b, c, d;

    
protected:
    bool EqualityCheck(const Tup4& other)
    {
        return (a == other.a && b == other.b && c == other.c && d == other.d);
    }

};

Then your Point4 and Vec4 classes can have overloaded equality operators that call the parent's method:

class Point4 : public Tup4
{
public:
    bool operator==(const Point4& other)
    {
        return EqualityCheck(other);
    }
};

CodePudding user response:

Tup4 is a concept not a class. Vec4 snd Point4 satisfy that concept.

Most of Vec4 and Point4 are implemented as templates.

In the rare case you need to handle Tup4s in runtime polymophic way, don't use inheritance, use type erasure like std function. But you probably won't.

struct Tup4Data{
  double v[4];
};
template<class D>
struct Tup4Impl:Tup4Data{
  // common implementation details of Tup4
  // D is derived class (Vec4 or Point4)
};

struct Vec4:Tup4Impl<Vec4>{
  // extra stuff for Vec4
};
struct Point4:Tup4Impl<Point4>{
  // extra stuff for Poimt4
};

Now, code that just wants to work on raw doubles and doesn't care can take Tup4Data. Tup4Impl uses the CRTP if you want to look it up; this provides static polymorphism.

Those that care if it is a vector or a point can take either one.

Those that wants to take both and behave differently can be template code, or type erase.

This last case -- type erase -- is harder, but in exchange you get massive improvements in every other case. And 99% of code bases don't even need to type erase.

I'm not even certain what kind of situation has code that wants to type erase here.

So just don't worry about it. (If you want to learn, look up example std function implementations).

CodePudding user response:

You can use templates for this.

It actually is not a good use OOP to shoehorn value-like types such as mathematical vectors and points into an object hierarchy. Object hierarchies mean using "reference semantics"; whereas, vectors, tuples, and points want to be values.

Look at, for example, how the C standard library implements complex numbers. It implements them as a class template parametrized on the number type you'd like to use e.g. float, double, etc. and then overloads the arithmetic operators to handle complex<T>.

How you would really implement a vector etc. class is similar.

  • Related