У меня есть эта старая реализация шаблона Command. Это своего рода передача контекста через всю реализацию DIOperation , но позже я понял, что в процессе обучения и обучения (что никогда не останавливается) это не оптимально. Я также думаю, что «посещение» здесь не совсем подходит и просто сбивает с толку.
Я на самом деле думаю о рефакторинге своего кода, в том числе потому, что команда не должна ничего знать о других, и в данный момент все они используют одни и те же пары ключ-значение. Действительно сложно определить, какому классу принадлежит какое значение ключа, что иногда приводит к дублированию переменных.
Пример варианта использования: допустим, для CommandB требуется UserName, который устанавливается CommandA . Должна ли CommandA установить ключ UserNameForCommandB = John ? Или они должны совместно использовать общее значение ключа UserName = John ? Что, если имя пользователя используется третьей командой?
Как я могу улучшить этот дизайн? Благодаря!
class DIParameters {
public:
/**
* Parameter setter.
*/
virtual void setParameter(std::string key, std::string value) = 0;
/**
* Parameter getter.
*/
virtual std::string getParameter(std::string key) const = 0;
virtual ~DIParameters() = 0;
};
class DIOperation {
public:
/**
* Visit before performing execution.
*/
virtual void visitBefore(DIParameters& visitee) = 0;
/**
* Perform.
*/
virtual int perform() = 0;
/**
* Visit after performing execution.
*/
virtual void visitAfter(DIParameters& visitee) = 0;
virtual ~DIOperation() = 0;
};
источник
Ответы:
Я немного обеспокоен изменчивостью параметров вашей команды. Действительно ли необходимо создать команду с постоянно меняющимися параметрами?
Проблемы с вашим подходом:
Хотите ли вы, чтобы другие потоки / команды меняли ваши параметры в процессе
perform
?Вы хотите, чтобы один
visitBefore
иvisitAfter
тот жеCommand
объект вызывался с разнымиDIParameter
объектами?Вы хотите, чтобы кто-то передавал параметры вашим командам, о которых команды не имеют представления?
Ничто из этого не запрещено вашим текущим дизайном. Хотя общая концепция параметра «ключ-значение» иногда имеет свои достоинства, она мне не нравится в отношении универсального класса команд.
Пример последствий:
Рассмотрим конкретную реализацию вашего
Command
класса - что-то вродеCreateUserCommand
. Теперь, очевидно, когда вы запрашиваете создание нового пользователя, команде понадобится имя для этого пользователя. Учитывая, что я знаю классыCreateUserCommand
иDIParameters
классы, какой параметр я должен установить?Я мог бы установить
userName
параметр, илиusername
.. вы относитесь к параметрам без учета регистра? Я не знаю, на самом деле .. о, подождите .. может быть, это простоname
?Как вы видите, свобода, которую вы получаете от общего сопоставления ключ-значение, подразумевает, что использование ваших классов в качестве того, кто их не реализовал, неоправданно сложно. По крайней мере, вам нужно предоставить некоторые константы для ваших команд, чтобы другие знали, какие ключи поддерживаются этой командой.
Возможны разные конструкции подходов:
Parameter
экземпляры в неизменяемые, вы можете свободно использовать их в различных командах.UserParameter
класс, который содержит именно те параметры, которые мне понадобятся для команд, в которых участвует пользователь, было бы намного проще работать с этим API. Вы по-прежнему можете наследовать параметры, но для командных классов больше не будет смысла принимать произвольные параметры - конечно, для профессионалов это означает, что пользователи API точно знают, какие параметры необходимы.visitBefore
иvisitAfter
, хотя они также использовались с разными параметрами, вы столкнетесь с проблемой вызова с разными параметрами. Если параметры должны быть одинаковыми при нескольких вызовах методов, вам необходимо включить их в команду, чтобы они не могли быть переключены для других параметров между вызовами.источник
Что хорошо в принципах дизайна, так это то, что рано или поздно они конфликтуют друг с другом.
В описанной ситуации, я думаю, я бы предпочел использовать некоторый контекст, из которого каждая команда может получать информацию и помещать информацию (особенно, если это пары ключ-значение). Это основано на компромиссе: я не хочу, чтобы отдельные команды были связаны просто потому, что они являются своего рода вводом друг для друга. Внутри CommandB мне все равно, как было задано имя пользователя - только то, что оно мне нужно использовать. То же самое в CommandA: я установил информацию, я не хочу знать, что с ней делают другие - ни кто они.
Это означает некий проходной контекст, который вы можете найти плохим. Для меня альтернатива хуже, особенно если этот простой контекст значения ключа (может быть простым компонентом с геттерами и сеттерами, чтобы немного ограничить коэффициент «свободной формы») может позволить решению быть простым и тестируемым, с хорошим отдельные команды, каждая со своей бизнес-логикой.
источник
Предположим, у вас есть командный интерфейс:
И предмет:
Что вам нужно это:
Установить
NameObserver& o
как ссылку на CommandB. Теперь, когда CommandA изменяет имя субъекта, CommandB может выполняться с правильной информацией. Если имя используется более команды, используйтеstd::list<NameObserver>
источник
Я не знаю, является ли это правильным способом справиться с этим на программистах (в этом случае я прошу прощения), но, проверив все ответы здесь (в частности, @ Frank's). Я реорганизовал свой код следующим образом:
Спасибо за помощь, и я надеюсь, что я не ошибся здесь :)
источник