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

мне интересно, как (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);
};

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

6 ответов


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

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

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


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

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


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

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

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

class InputData {};
class OutputData {};

class TheAlgorithm 
{
private:
     //functions and common data

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

тогда пусть это взаимодействует с main () или вашим GUI, как вы хотите.


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

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

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

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

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

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

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.
    }
}

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

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