Хорошая практика для случайного выбора алгоритма с C ++ - PullRequest
6 голосов
/ 16 августа 2010

Настройка:
Должен быть создан псевдослучайный образец.Существует несколько способов / или алгоритмов для создания различного контента.Все алгоритмы будут генерировать список символов (но может быть что угодно) ... важная часть заключается в том, что все они возвращают одинаковые типы значений и нуждаются во входных аргументах одинакового типа.

Этодолжна быть возможность вызова метода GetRandomPattern (), который будет использовать случайный один из алгоритмов при каждом его вызове.

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

class PatternGenerator{
 public:
  list<char> GetRandomPattern();
 private:
  list<char>GeneratePatternA(foo bar);
  list<char>GeneratePatternB(foo bar);
  ........
  list<char>GeneratePatternX(foo bar);
}

Что было бы хорошим способом выбора случайной функции GeneratePatternкаждый раз, когда вызывается метод GetRandomPattern () ?

Или весь класс должен быть оформлен по-другому?

Большое спасибо

Ответы [ 4 ]

9 голосов
/ 16 августа 2010

Создайте отдельный класс для каждого алгоритма, каждый из которых подклассирует класс генератора. Поместите экземпляры этих объектов в список. Выберите один случайным образом и используйте его!

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

Обновление Не могу удержаться от споров о объектно-ориентированном решении после того, как предложение указателя пришло

  • Представьте, что в какой-то момент вы хотите напечатать, какой метод создал какую-то случайную вещь. С объектами это легко, просто добавьте метод «name» или что-то в этом роде. Как вы хотите добиться этого, если все, что у вас есть, это указатель? (да, создайте словарь из указателей на строки, хм ...)
  • Представьте, что вы узнали, что у вас есть десять методов, пять из которых отличаются только параметром. Таким образом, вы пишете пять функций «просто чтобы сохранить код чистым от мусора ООП»? Или вы не захотите иметь функцию, которая может хранить какое-то состояние (также известное как объект?)
  • То, что я пытаюсь сказать, это то, что это учебное приложение для некоторого ООП-дизайна. Вышеприведенные пункты просто пытаются уточнить это и доказать, что, даже если он работает с указателями сейчас, это не решение для будущего. И вы не должны бояться создавать код, который будет общаться с читателем (то есть с вашим будущим, через четыре недели или около того), сообщая этому человеку, что он делает
5 голосов
/ 16 августа 2010

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

#define FUNCTION_COUNT 24
typedef list<char>(*generatorFunc)(foo);

class PatternGenerator{
    public:
        PatternGenerator() {
            functions[0] = &GeneratePatternA;
            functions[1] = &GeneratePatternB;
            ...
            functions[24] = &GeneratePatternX;
        }
        list<char> GetRandomPattern() {
            foo bar = value;
            int funcToUse = rand()%FUNCTION_COUNT;
            functions[funcToUse](bar);
        }
    private:
        generatorFunc functions[FUNCTION_COUNT];
}
1 голос
/ 16 августа 2010

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

Если бы я использовал Ruby / Java / C #, я бы определился с предложенным шаблоном Стратегического дизайна; -)

class PatternGenerator{
 typedef list<char>(PatternGenerator::*createPatternFunctionPtr);
public:
 PatternGenerator(){
  Initialize();
   }
 GetRandomPattern(){
  int randomMethod = (rand()%functionPointerVector.size());
  createPatternFunctionPtr randomFunction = functionPointerVector.at( randomMethod );
  list<char> pattern = (this->*randomFunction)();
  return pattern;
  }
private:
  void Initialize(){
   createPatternFunctionPtr methodA = &PatternGenerator::GeneratePatternA;
   createPatternFunctionPtr methodB = &PatternGenerator::GeneratePatternB;
   ...
   functionPointerVector.push_back( methodA );
   functionPointerVector.push_back( methodB );
   }
  list<char>GeneratePatternA(){
   ...}
  list<char>GeneratePatternB(){
   ...}
  vector< createPattern > functionPointerVector;

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

1 голос
/ 16 августа 2010

Один из способов избежать подобного переключателю кодирования - использовать шаблон проектирования стратегии. Как пример:
</p> <pre> class IRandomPatternGenerator { public: virtual list<int> makePattern(foo bar); }; class ARandomPatternGenerator : public IRandomPatternGenerator { public: virtual list<int> makePattern(foo bar) { ... } }; class BRandomPatternGenerator : public IRandomPatternGenerator { public: virtual list<int> makePattern(foo bar) { ... } }; </pre> <p>

Затем вы можете выбрать конкретный алгоритм в зависимости от типа времени выполнения вашего экземпляра RandomPatternGenerator. (Как пример создания списка, как предложил nicolas78)

...