2016-05-10 2 views
0

Я неспециалист с точки зрения дизайна программного обеспечения. Я столкнулся с «проблемой», которая, возможно, решается какой-то известной техникой/идиомой/шаблоном, о которой мне бы хотелось рассказать.Разработка программного обеспечения: Слишком много классов?

У меня есть абстрактный базовый класс, в основном определяющий одну чистую виртуальную функцию-член и немного больше. Тогда у меня есть несколько классов, происходящих из этого, и переопределение упомянутой виртуальной функции. У меня сейчас полдюжины таких классов, и число растет. Классы имеют только несколько членов данных (очень немногие, например, пара удвоений или что плюс указатель на функцию), и они отличаются главным образом тем, как они выполняют очень короткие вычисления. Интересно, указывает ли это на плохой дизайн и лучше разбирается каким-то другим способом.

При необходимости, может кто-то указать мне на соответствующий шаблон дизайна или идиому, о которой я должен знать. Благодарю.

EDIT

Чтобы прояснить вещи, абстрактный базовый класс не имеет каких-либо элементов данных. Не все производные классы имеют члены данных. То, что я делаю, это преобразование координат для интегралов как классов. Для данного преобразования требуется только пара параметров, а иногда и пользовательская функция.

+0

Если код не дублируется, то вы на правильном пути. – Dialecticus

+0

Имеет ли абстрактный базовый класс какие-либо элементы данных? –

+1

Согласитесь с @Dialecticus, но указатель функции поднимает бровь. Почему указатель функции? Не можете ли вы сконструировать это в специфику данного подкласса? В стороне, если вы еще этого не сделали, посмотрите [std :: function'] (http://en.cppreference.com/w/cpp/utility/functional/function). Это может упростить использование указателя функции. – user4581301

ответ

2

Если ваш абстрактный базовый класс не имеет каких-либо элементов данных (и, похоже, он не должен, если он имеет один чистый виртуальный метод), то для этого есть лучший образец. Предположим, у нас есть этот код:

struct AbstractBase { 
    virtual double calc(double) = 0; 
    virtual ~AbstractBase() = default 
} 

Теперь, вы должны наследоваться от этого, чтобы использовать вещи динамически в другом месте:

struct Derived : public AbstractBase { ... } 

void BaseUser(AbstractBase& ab) { ... }; 

Менее сочетании решение было бы просто написать свой класс как функция объект и использование std::function.

struct Derived { 
    double operator()(double x) { ... }; 
} 

void User(std::function<double(double)> f); 

User(Derived{}); // Calls user with Derived routine. 

Это имеет и другие преимущества, например, если некоторые из ваших производных классов на самом деле не нужно состояние, то вы можете просто написать их как обычные функции и по-прежнему передавать их вокруг в std::functions.Еще одно преимущество заключается в том, что вы можете писать короткие функции рядный, а теперь, потому что лямбда является функцией объекта:

User([] (double x) { return 2*x; }); 

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

Меня не волнует количество производных объектов, которые у вас обязательно есть.

+0

И даже если ваши функции нуждаются в состоянии - вы можете записать их как lambdas, захватив из контекста то, что им нужно, когда они построены, и передать их как функции ... или как лямбды! – davidbak

+0

Да, абстрактный базовый класс не имеет каких-либо элементов данных. Кроме того, не все производные классы имеют членов. Фактическая вещь, которую я делаю, это преобразование координат для интеграла как классов. Некоторые могут получить функцию от пользователя. Я отредактирую OP, чтобы уточнить это. – booNlatoT

+0

@ davidbak Да, но если у абстрактного базового класса OP было состояние, тогда это невозможно было бы смоделировать с помощью этого дизайна. Мой комментарий был конкретно о состоянии в базовом классе, а не в реализациях. –

1

Есть поговорка: СУХОЙ. D не вкл. R epeat Y само устройство. Помимо переопределения этого чистого виртуального метода, если есть какая-либо другая форма дублирующего кода, это может быть признаком того, что требуется ревизия. Если у вас много классов, и каждый из них уникален по функциональности, тогда все в порядке.

0

Если операции могут быть составлены, вы должны попытаться составить объекты для составления операций. Класс должен действительно делать только одно, поэтому у вас нет проблем.

+0

Я бы немного передумал. Класс должен представлять одну вещь. Это может многое сделать. – user4581301

+0

Вы правы, функция должна делать одно. – dasPing

1

Трудно полностью прокомментировать, не видя примера. В общем, тот факт, что у вас много классов, каждый из которых выполняет только небольшую (множество) операций, не обязательно указывает на плохой дизайн. Вы должны стараться как можно больше создавать классы, используя DRY (не повторяйте себя) и SOLID (см. this wikipedia article).

+0

Спасибо за ссылку. – booNlatoT

3

абстрактный базовый класс с одной виртуальной функцией звучит почти точно как std::function<>, содержащий лямбда.

, например:

#include <functional> 
#include <vector> 
#include <iostream> 

int main() 
{ 
    using op = std::function<int()>; 

    int x = 7; 
    int y = 5; 
    auto a = [x, y]() -> int { return x + y; }; 
    auto b = [x, y]() -> int { return x - y; }; 

    auto ops = std::vector<op> { a, b }; 
    for (const auto& o : ops) 
    { 
     std::cout << o() << std::endl; 
    } 
} 

В конце концов, лямбда это просто сокращенная форма записи класса, который захватывает (копию) некоторые объекты или ссылки на объекты и выставляет оператор вызова.

STD :: function является полиморфным адаптером для таких классов.

Использование lambdas может сэкономить вам некоторое набрав. Является ли это добавлением или вычитанием семантического выражения вашего кода (что, возможно, является более важным), является другим вопросом.

+0

И вам даже не нужна функция std ::, если вы придерживаетесь 'auto' и' decltype', вы можете использовать «голые» лямбды, которые, я понимаю, могут быть более компактными и эффективными, чем std :: function, если вы не нужно общности, которая обеспечивает. – davidbak

+0

@ davidbak Я предполагаю, что OP нуждается в полиморфном интерфейсе, так как он использует ABC. Без примера кода невозможно сказать. –

+0

Да, пример кода был бы приятным. Я получаю то, что вы говорите о полиморфном интерфейсе. – davidbak

Смежные вопросы