Инкапсулирование алгоритма в класс

Мне интересно, как (un) распространять инкапсуляцию алгоритма в класс? Более конкретно, вместо того, чтобы иметь несколько отдельных функций, которые пересылают общие параметры между собой:

void f(int common1, int param1, int *out1);
void g(int common1, int common2, int param1, int *out2)
{
  f(common1, param1, ..);
}

для инкапсуляции общих параметров в класс и выполнения всей работы в конструкторе:

struct Algo
{
  int common1;
  int common2;

  Algo(int common1, int common2, int param)
  { // do most of the work }

  void f(int param1, int *out1);
  void g(int param1, int *out2);
};

Кажется очень практичным не пересылать общие параметры и промежуточные результаты через аргументы функции. Но я не видел широко используемого "шаблона". Каковы возможные недостатки?

Ответ 1

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

Конечно, в любое время, когда вы начинаете строить сложную структуру классов, будет добавлена ​​дополнительная архитектура - и, следовательно, код - которую нужно будет создавать и поддерживать. Однако, на мой взгляд, выгоды в долгосрочной перспективе перевешивают это незначительное неудобство.

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

Ответ 2

Там шаблон дизайна, который решает проблему; он называется "Шаблон разработки стратегии" - вы можете найти хорошую информацию об этом здесь.

Хорошая вещь о "Стратегии" заключается в том, что она позволяет вам определить семейство алгоритмов, а затем использовать их взаимозаменяемо, не изменяя клиентов, которые используют алгоритмы.

Ответ 3

Может ли ваш вопрос быть более обобщенным, например: "Как мы используем объектно-ориентированный дизайн, когда основная идея программного обеспечения просто запускает алгоритм?"

В этом случае я думаю, что такой дизайн, как вы предлагали, является хорошим первым шагом, но эти вещи часто зависят от проблем.

Я думаю, что хороший общий дизайн похож на то, что у вас есть.,.

class InputData {};
class OutputData {};

class TheAlgorithm 
{
private:
     //functions and common data

public:
   TheAlgorithm(InputData);      
   //other functions
   Run();
   ReturnOutputData();
};

Затем пусть это взаимодействует с основным() или вашим графическим интерфейсом, но вы хотите.

Ответ 4

Я обычно создаю функтор или Объект функции для инкапсуляции моих алгоритмов.

Обычно я использую следующий шаблон

class MyFunctor {
    public:
       MyFunctor( /* List of Parameters */ );
       bool execute();
    private:
       /* Local storage for parameters and intermediary data structures */
}

Затем я использовал мои функции таким образом:

    bool success = MyFunctor( /*Parameter*/ ).execute();

Ответ 5

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

public class MyFooConfigurator
{
   public MyFooConfigurator(string Param1, int, Param2) //Etc...
   {
      //Set all the internal properties here
      //Another option would also be to expose public properties that the user could
      //set from outside, or you could create a struct that ecapsulates all the
      //parameters.
      _Param1 = Param1; //etc...
    }

    Public ConfigureFoo()
    {
       If(!FooIsConfigured)
          return;
       Else
          //Process algorithm here.
    }
}

Ответ 6

Если вам когда-нибудь понадобится вызвать оба метода с параметрами конструктора, тогда я сделаю это.

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