Одноразовый класс - PullRequest
2 голосов
/ 07 июля 2011

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

Это плохой способ делать вещи?Есть ли лучший шаблон дизайна для такого рода вещей?Или я должен просто сдаться и заставить пользователя передать множество ссылочных параметров, чтобы вернуть кучу вещей через?

Ответы [ 6 ]

4 голосов
/ 07 июля 2011

То, что вы описываете, это не класс (состояние + методы для его изменения), , но алгоритм (входные данные карты для вывода данных):

result_t do_it(parameters_t);

Почему вы думаете, что вам нужен класс для этого?

3 голосов
/ 07 июля 2011

Звучит так, будто ваш класс - это в основном блок параметров в тонкой маскировке.

В этом IMO нет ничего плохого, и это, безусловно, лучше, чем функция с таким количеством параметров, которую трудно отследить, какая есть какая.

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

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

  • Как предполагает Конрад, это, возможно, вводит в заблуждение. Метод сброса не следует рассматривать как замену для конструктора - это задача конструкторов переводить объект в самосогласованное инициализированное состояние, а не методы сброса. Объект должен быть самосогласованным всегда.
  • Если нет причины для выполнения кумулятивных вызовов do-it в стиле "общий ход", вызывающему абоненту никогда не придется явно вызывать сброс - он должен быть встроен в вызов do-it в качестве первого шага.
  • Я все же решил, поразмыслив, вычеркнуть это - не столько из-за комментария Джальфа, но из-за волос, которые мне пришлось разделить, чтобы спорить ;-) - По сути, я считаю, У меня почти всегда есть метод сброса для этого стиля класса, отчасти потому, что у моих «инструментов» обычно есть несколько связанных видов «сделать это» (например, «вставить», «поиск» и «удалить» для инструмента дерева), и общий доступ Режим. Режим - это просто некоторые поля ввода, в терминах блоков параметров, но это не значит, что я хочу продолжать повторную инициализацию. Но то, что этот шаблон часто случается со мной, не означает, что это должно быть принципиальным моментом.

У меня даже есть название для этих вещей (не ограничиваясь случаем с одной операцией) - классы "инструмента". «Tree_searching_tool» будет классом, который ищет (но не содержит) дерево, например, хотя на практике у меня будет «tree_tool», который реализует несколько операций, связанных с деревом.

По сути, даже блоки параметров в C в идеале должны обеспечивать своего рода абстракцию, которая придает ему некоторый порядок, а не просто набор параметров. «Инструмент» - это (расплывчатая) абстракция. Классы являются основным средством обработки абстракции в C ++.

2 голосов
/ 07 июля 2011

Я использовал аналогичный дизайн и тоже удивлялся этому. Придуманный упрощенный пример может выглядеть так:

FileDownloader downloader(url);
downloader.download();
downloader.result(); // get the path to the downloaded file

Чтобы сделать его многоразовым, я храню его в boost::scoped_ptr:

boost::scoped_ptr<FileDownloader> downloader;

// Download first file
downloader.reset(new FileDownloader(url1));
downloader->download();

// Download second file
downloader.reset(new FileDownloader(url2));
downloader->download();

Чтобы ответить на ваш вопрос: я думаю, что все в порядке. Я не нашел никаких проблем с этим дизайном.

1 голос
/ 07 июля 2011

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

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

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

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

0 голосов
/ 07 июля 2011

Я не согласен с тем, что описываемый вами класс "не является нормальным классом". У него есть состояние и поведение. Вы указали, что у него относительно короткий срок службы, но это не делает его менее классным.

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

Краткосрочные классы: создание новых и повторное использование экземпляров : Есть много примеров, когда экземпляры классов используются повторно: пулы потоков, пулы соединений с БД (вероятно, чертовски близко к любой программной конструкции, заканчивающейся на 'пул' :). По моему опыту, они, кажется, используются, когда создание объекта является дорогой операцией. Ваши маленькие, недолговечные классы не похожи на то, что их дорого создавать, поэтому я не стал бы пытаться использовать их повторно. Вы можете обнаружить, что какой бы механизм объединения вы ни внедрили, на самом деле стоит БОЛЬШЕ (с точки зрения производительности), чем просто создание новых экземпляров при необходимости.

0 голосов
/ 07 июля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...