Инкапсуляция алгоритма в класс - PullRequest
11 голосов
/ 30 сентября 2008

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

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 ]

5 голосов
/ 30 сентября 2008

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

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

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

5 голосов
/ 30 сентября 2008

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

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

2 голосов
/ 30 сентября 2008

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

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

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

class InputData {};
class OutputData {};

class TheAlgorithm 
{
private:
     //functions and common data

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

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

1 голос
/ 30 сентября 2008

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

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.
    }
}
1 голос
/ 30 сентября 2008

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

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

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

Тогда я использовал свои функторы так:

    bool success = MyFunctor( /*Parameter*/ ).execute();
0 голосов
/ 30 сентября 2008

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

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

...