Я использую шаблон посетителя для определения набора операций над некоторыми классами.
Некоторые операции являются коммутативными, поэтому я получаю дублирование в шаблоне шаблона посетителя.
Предположим, что у меня есть классы A B C и операции: A * A, A * B, A * C, B * A, B * B, B * C, C * A, C * B, C * C.
A * A, B * B, C * C уникальны.
A * B, B * A и друзья будут иметь дублирование кода Я мог бы реализовать A * B и сделать B * A A * B, но я в конечном итоге спрошу себя: в каком файле я реализовал операцию между A и B снова, в A или в B? (будет около 6 классов, поэтому я задам этот вопрос много. 15 пар возможных операций)
Существует риск того, что кто-то в будущем сделает бесконечный цикл A * B, вызывающий B * A, вызывающий A * B при реализации новой операции.
Неестественно иметь соглашение, которое решает, какие из них должны быть реализованы A * B или B * A.
Я мог бы сделать 3-й файл со всеми реализованными функциями, которые вызывают либо A * B, и B * A, не выглядит очень объектно-ориентированным.
Как бы вы решили эту проблему?
Благодаря
(я мог бы перечислить некоторый код, но это долго и не иллюстрирует точку легко)симметричный шаблон посетителя
ответ
Вы правы, вы определенно должны воздерживаться от осуществления A*B
как вызов B*A
. В дополнение к потенциалу создания бесконечной цепочки вызовов этот подход не отражает симметрию операции в вашем коде, потому что код не симметричен.
Лучшим подходом является реализация «симметричной» операции в вспомогательном классе или в качестве функции верхнего уровня, в зависимости от того, что поддерживается на вашем языке, а затем сделать так, чтобы и вспомогательная реализация выполняла как A*B
, так и B*A
.
Мое предложение было бы использовать конструктор, который будет выступать в качестве параметра Builder
new ParameterBuilder()
.setFirst("A")
.setSecond("B")
.setThird("C")
...
.build();
Тогда вы будете иметь только один метод, который принимает в качестве аргумента ParameterBuilder
.
hmm, я мог бы реализовать материал как «Object * result = new MultiplyHelper(). AddOperand (« A »). AddOperand (« B »). Compute();' – titus
У меня также есть некоторые некоммутативные функции, такие как A/B , используя симметричную конструкцию, как указано выше для несимметричной операции, не является нормально. Лучше наоборот. – titus
Конструктор параметров - это просто для того, чтобы принимать параметры, что вы делаете с ними, зависит от вас. То, что вы делаете, тоже хорошо. Я предложил это решение, чтобы сохранить ваши методы неповрежденными и только изменить подпись метода. –
есть правило, что класс не подходит для использования атрибутами других классов. Я думаю, что выбора не так много. – titus
Скотт Мейер отлично обсуждает эту тему в своей более эффективной книге на C++ (пункт 31: «Выполнение функций виртуально по отношению к нескольким объектам»). Его пример - создание функции столкновения для игра, в которой различные объекты могут сталкиваться в пространстве. Его пример тоже симметричен (неважно, попадает ли астероид на космический корабль или космический корабль попадает на астероид, взрыв такой же). Он начинает с шаблона посетителя и постепенно разрабатывает C++-специфическое решение, основанное на RTTI. – dasblinkenlight
это в издании 1996 года? – titus