Как применить Template Method Pattern для функций с разными сигнатурами?

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

/* existing library functions */
/* the signatures are different: some return int, some float */

/* set of input related functions */
int getInputValue() { return 42; }
size_t getInputSize() { return 1; }

/* set of output related functions */
int getOutputValue() { return 21; }
size_t getOutputSize() { return 1; }

/* set of parameter related functions */
float getParameterValue() { return 3.14; }
size_t getParameterSize() { return 1; }

и предположим, что они используются одинаково:

if (getSize() > 0) {
T value = getValue()

А) Какой хороший способ предоставить getSize() а также getValue()?

Я сначала хоть что Шаблон Метод Шаблон это то, что я хочу, но я не смог применить его, потому что, в отличие от Worker в шаблоне Template Template, мои функции имеют разные подписи.

Итак, что я сделал вместо этого:

/* I want to provide a uniform interface */

/* the specific part of inputs, outputs and parameters is in the traits */
struct input_traits {
typedef int value_type;
static int getValue() { return getInputValue(); }
static size_t getSize() { return getInputSize(); }
};

struct output_traits {
typedef int value_type;
static int getValue() { return getOutputValue(); }
static size_t getSize() { return getOutputSize(); }
};

struct parameter_traits {
typedef float value_type;
static float getValue() { return getParameterValue(); }
static size_t getSize() { return getParameterSize(); }
};/* the common part (they are used in the same way) is in the Helper */
template<typename traits>
class CommonUsage {
public:
void use()
{
if (traits::getSize() > 0) {
typename traits::value_type value = traits::getValue();
}
}
};

int main()
{
CommonUsage<input_traits>().use();
CommonUsage<output_traits>().use();
CommonUsage<parameter_traits>().use();
}

Б) Это хороший подход?

2

Решение

О. Если я правильно понял ваш вопрос, вы должны использовать абстрактный класс.
Посмотрите на следующий код, он по сути делает то же самое, что и ваш код.

Вот как бы я это сделал.

#include <iostream>

template <typename value_type>
class Traits {
public:
virtual value_type getValue() const = 0;
virtual size_t getSize() const = 0;

virtual ~Traits() { }
};

class input_traits: public Traits <int>{
public:
virtual int getValue() const {
return 42;
}

virtual size_t getSize() const {
return 1;
}
};

class parameter_traits: public Traits <double>{
public:
virtual double getValue() const {
return 3.14;
}
virtual size_t getSize() const {
return 1;
}
};

class CommonUsage {
public:
template <typename value_type>
void use(const Traits<value_type>& traitsObject) {
if (traitsObject.getSize() > 0) {
std::cout << traitsObject.getValue();
}
}
};

int main() {
CommonUsage().use(parameter_traits());
return 0;
}
2

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

Как альтернатива ответу пользователя (на этот раз с использованием специализации шаблона):

template <class T>
struct traits {
T getValue() const {  throw std::runtime_exception("..."); }
size_t getSize() const { return 0; }
};template <>
struct traits<int> {
int getValue() const { return 42; }
size_t getSize() const { return 1; }
};

template <>
struct traits<float> {
int getValue() const { return 3.145; }
size_t getSize() const { return 1; }
};

// do template aliasing
using input_traits = traits<int>;
using parameter_traits = traits<float>;

struct CommonUsage {
template <typename T>
static void use(const traits<T> &traits) {
if (traits.getSize() > 0)
std::cout << traits.getValue() << std::endl;
}
};

int main(int arg, char **argv) {
CommonUsage::use(input_traits());
CommonUsage::use(parameter_traits());
}

Есть преимущества / недостатки обоих подходов. Если вы используете специализацию шаблонов, вы не платите за виртуальные методы.

0

По вопросам рекламы [email protected]