У меня есть эти два похожих класса. Какой дизайн вы бы использовали для факторизации кода?

У меня довольно сложная графоподобная структура данных. Для ясности, давайте упростим это:

class Node;
class AbstractEdge { void foo() {} };
class Edge1: public AbstractEdge { void bar1() {} };
class Edge2: public AbstractEdge { void bar2() {} };

Как видите, наш график не похож на любой другой граф: существует два вида ребер, оба наследуются от AbstractEdge. Этот дизайн не может быть изменен. Теперь предположим, что мне нужно спроектировать два класса в соответствии с:

class OrientedEdge1
{
Edge1 * edge;
bool orientation;

void foo() { edge->foo(); }
void bar1() { edge->bar1(); }
}

class OrientedEdge2
{
Edge2 * edge;
bool orientation;

void foo() { edge->foo(); }
void bar2() { edge->bar2(); }
}

На практике OrientedEdge1 :: foo () и OrientedEdge2 :: foo () намного длиннее, чем просто вызов одного метода, но идея в том, что они идентичны, вызывая только методы, унаследованные от AbstractEdge.

Какой дизайн вы бы использовали для факторизации кода? Я думаю о трех подходах:

foo_impl(AbstractEdge * edge) { edge->foo(); }

class OrientedEdge1
{
Edge1 * edge;
bool orientation;

void foo() { foo_impl(edge); }
void bar1() { edge->bar1(); }
}

class OrientedEdge2
{
Edge2 * edge;
bool orientation;

void foo() { foo_impl(edge); }
void bar2() { edge->bar2(); }
}

Плюсы:

  • Очень простое решение, намного лучше, чем не факторинг.

Минусы:

  • Не все методы могут быть реализованы как свободные функции.

  • Код объявления все еще дублируется.

class AbstractOrientedEdge
{
AbstractEdge * edge;
bool orientation;

void foo() { edge->foo(); }
}

class OrientedEdge1: public AbstractOrientedEdge
{
Egde1 * edge1() { return static_cast<Egde1*>(edge); }

void bar1() { edge1()->bar1(); }
}

class OrientedEdge2: public AbstractOrientedEdge
{
Egde2 * edge2() { return static_cast<Egde2*>(edge); }

void bar2() { edge2()->bar2(); }
}

Плюсы:

  • Больше факторизации.

  • Я не планирую использовать эти два класса полиморфно, но кто знает, может быть, тот факт, что они связаны наследованием, может оказаться полезным в будущем.

Минусы:

  • Нужна осторожность в конструкторах / установщиках, чтобы OrientedEdge1 :: edge всегда указывал на Egde1 *.

  • Почему-то static_cast чувствует себя не так.

template <class EdgeT>
class OrientedEdge
{
EdgeT * edge;
bool orientation;

void foo() { edge->foo(); }
}

class OrientedEdge1: public OrientedEdge<Edge1>
{
void bar1() { edge->bar1(); }
}

class OrientedEdge2: public OrientedEdge<Edge2>
{
void bar2() { edge->bar2(); }
}

Плюсы:

  • Большая факторизация.

  • Указатель, хранящийся в обоих классах, имеет правильный тип, приведение не требуется.

Минусы:

  • Необходимо сохранить реализацию общего кода в заголовке, что заставляет включать AbstractEdge.h (можно было бы избежать с помощью предварительных объявлений с предыдущими подходами).

Вопросы: Какой подход вы бы использовали? Есть ли у вас какие-либо другие решения или предложения?

0

Решение

1 и 3.

3 удаляет дубликаты шаблонов, 1 перемещает реализацию куда угодно.

1

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector