Removing duplicate code

This is a very short follow-up to the solution for removing duplicate code based on C++ policy-based design.

As I mentioned in that article, there are times when you need something more simple. And I can think of two other solutions.

The starting point is the duplication of lines 15 and 27:

#include <cassert>

struct Object {
    int prop_1{};
    int prop_2{};
    int prop_3{};
};

void set_object_properties_1(Object& object)
{
    if (object.prop_1 == 0) {
        object.prop_1 = 1;
    }

    object.prop_2 = object.prop_1 * 4;  // duplicate

    object.prop_3 = object.prop_2 * 5 + object.prop_1 * 2;
}

void set_object_properties_2(Object& object)
{
    if (object.prop_1 == 0) {
        object.prop_1 = 3;
    }
    object.prop_1 *= 2;

    object.prop_2 = object.prop_1 * 4;  // duplicate

    object.prop_3 = object.prop_1 * object.prop_2;
}

int main()
{
    Object object_1{};
    set_object_properties_1(object_1);
    assert(object_1.prop_1 == 1);
    assert(object_1.prop_2 == 4);
    assert(object_1.prop_3 == 22);

    Object object_2{};
    set_object_properties_2(object_2);
    assert(object_2.prop_1 == 6);
    assert(object_2.prop_2 == 24);
    assert(object_2.prop_3 == 144);
}

Tag dispatching

This method introduces two new types, each associated with a use case. Objects of these new types are being passed down the flow to know exactly which case to handle. It’s using overloading of the functions that need to behave depending on the use case.

struct Object1Tag {
};
void init_prop_1(Object1Tag, Object& object)
{
    if (object.prop_1 == 0) {
        object.prop_1 = 1;
    }
}

void init_prop_3(Object1Tag, Object& object) { object.prop_3 = object.prop_2 * 5 + object.prop_1 * 2; }

struct Object2Tag {
};
void init_prop_1(Object2Tag, Object& object)
{
    if (object.prop_1 == 0) {
        object.prop_1 = 3;
    }
    object.prop_1 *= 2;
}

void init_prop_3(Object2Tag, Object& object) { object.prop_3 = object.prop_1 * object.prop_2; }

template <typename Tag>
void set_object_properties_by_tag_dispatching(Tag tag, Object& object)
{
    init_prop_1(tag, object);

    object.prop_2 = object.prop_1 * 4;

    init_prop_3(tag, object);
}

When set_object_properties_by_tag_dispatching is called, it just needs to know the case. The API user directly provides the case “tag”.

Object object_1e{};
set_object_properties_by_tag_dispatching(Object1Tag{}, object_1e);

Object object_2e{};
set_object_properties_by_tag_dispatching(Object2Tag{}, object_2e);

I’ve previously mentioned that introducing a new parameter to a function is not desired because of the new responsibilities it creates. This time, the parameter is actually used only to know what function to call. Its value is not used to make different runtime decisions.

Inheritance

It’s very similar to tag dispatching because it’s also based on function overloading. The difference is who distinguishes between the overloads. Instead of creating new types as tags, you create new types as children of Object.

struct Object1 : Object {
};
void init_prop_1(Object1& object)
{
    if (object.prop_1 == 0) {
        object.prop_1 = 1;
    }
}

void init_prop_3(Object1& object) { object.prop_3 = object.prop_2 * 5 + object.prop_1 * 2; }

struct Object2 : Object {
};
void init_prop_1(Object2& object)
{
    if (object.prop_1 == 0) {
        object.prop_1 = 3;
    }
    object.prop_1 *= 2;
}

void init_prop_3(Object2& object) { object.prop_3 = object.prop_1 * object.prop_2; }

template <typename T>
void set_object_properties_by_child(T& object)
{
    init_prop_1(object);

    object.prop_2 = object.prop_1 * 4;

    init_prop_3(object);
}

Then, you create the children and call the property setter for each of them.

Object1 object_1f{};
set_object_properties_by_child(object_1f);

Object2 object_2f{};
set_object_properties_by_child(object_2f);

Diversity

There are even more ways to solve this issue, I suppose. The biggest win that I see in exploring these apparently small topics is learning. It’s the foundation for solving the big issues that you encounter in real life code.

I would enjoy seeing new solutions.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.