шаблон команды, возвращающий статус - PullRequest
38 голосов
/ 20 июля 2009

Однажды у меня была дискуссия о дизайне относительно шаблона команды. Мой коллега заявил, что объект команды не должен возвращать статус (успешный, неудачный и почему) после вызова метода .execute (). Причина в том, что вас не должно беспокоить, выполняется команда или нет, потому что команда не должна содержать состояния. Однако после вызова необходимо проверить, имела ли команда ожидаемый эффект. Другой аргумент, который он утверждал, состоял в том, что в «Банде четырех» шаблон команд не представляет этот случай (возвращения статуса).

Я утверждал обратное. GoF не представляет этот случай, но шаблон может быть смоделирован в соответствии с вашими потребностями. Если команда не выполнена, вызывающий клиент должен получить подтверждение статуса и в конечном итоге развернуть соответствующую реакцию. Вынуждая клиента проверить, было ли достигнутое действие успешным, он подвержен ошибкам и выдает дублированный код. Более того, бывают случаи, когда команда выдает результат (например, команда, которая добавляет строку к графику, каким-то образом будет иметь идентификатор строки для возврата клиенту) и притворяется, что у нее есть команды без состояния, означало, что вам пришлось «выловить» новый идентификатор объекта из модели данных.

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

Ответы [ 9 ]

26 голосов
/ 20 июля 2009

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

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

13 голосов
/ 26 июля 2009

В вопросе два вопроса с несколькими ответами :) Первый вопрос: должна ли команда возвращать состояние ошибки?

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

Одна из вещей, о которой вам нужно подумать:

  • Добавляю ли я больше связей ко многим командам и клиентам только для некоторых конкретных случаев ошибок?

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

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

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

Второй вопрос: должна ли команда иметь состояние?

Существуют архитектуры, где команде нужно состояние, а некоторые - где ей не нужно.

Некоторые возможности решить это:

  • Если вы хотите отменить свою команду, команды должны иметь состояние.
  • Если команды используются только для сокрытия способа, функция, которая работает с небольшим набором параметров, и результаты зависят только от той же самой команды, что и шаблон состояния, нет необходимости в состоянии, и вы можно использовать один и тот же объект снова и снова.

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

  • ... Если есть что-то, что, по вашему мнению, должно быть в этом списке, оставьте комментарий.
7 голосов
/ 23 июля 2009

Я буду ссылаться на "Head First Design Patterns". Примеры, которые они используют для шаблона команд:

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

Очевидно, что в первом случае получатель создает какое-то состояние: «вот хрень» или «у нас нет ржаного хлеба». В шикарном ресторане вы можете сделать это с помощью обработки исключений на более высоком уровне (maitre d 'подходит к столу и приносит извинения, предлагает замену и составляет ваш десерт), и обслуживающий персонал должен ничего не делать, кроме как правильно вызывать команды. Но в закусочной, может быть, повар идет вперед и заменяет черный хлеб - обслуживающий персонал (и клиент) должны быть в состоянии справиться с этим, не глядя на стойку, задаваясь вопросом: «Где мой тунец на ржи?» Эта книга не рассматривается непосредственно в книге, но я думаю, что это вполне обоснованный случай.

Но во втором сценарии призыватель намеренно сделан глупым. Если у вас что-то не так, это не выдаст ошибку, просто не даст никакого эффекта. Все смарты находятся в клиенте, чтобы определить, была ли его команда выполнена своевременно («Черт, я забыл его подключить»), или в приемнике, чтобы выяснить, что делать («Воспроизвести компакт-диск: закрыть лоток для компакт-дисков». первый ").

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

4 голосов
/ 26 марта 2011

Очень хорошая дискуссия. Я был на этом философском вопросе в течение нескольких часов, и я нашел решение, которое удовлетворяет мою одержимость. (Причина, по которой я люблю этот материал, состоит в том, что он сочетает в себе конкретную и абстрактную логику - логические + конструкции.)

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

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

Вот пример кода. Я не писал командный класс, потому что думаю, что вы получите идею без него. Его метод execute () вызывает метод run () получателя.

Клиент:

Class ClientType{

    CommandType m_Command;
    ReceiverType m_Receiver;
    boolean m_bResult;

    ClientType(){

      m_Receiver = new ReceiverType(this);
      m_Command = new CommandType(m_Receiver);
    }

    public void run(){  
            ... 
      m_Command.execute();
    }


    /*  Decoupled from both the   
     *  command and the receiver. 
     *  It's just a public function that
     *  can be called from anywhere. /
    public setResult(boolean bResult){
      m_bResult = bResult;
    }
}

Получатель:

Class ReceiverType{

    ClientType m_Client;
    boolean m_bResult;

    ReceiverType(ClientType client){
      m_Client = client;
    }

    public void run(){
            ...
      m_Client.setResult(m_bResult);    
    }
}

На первый взгляд может показаться, что я нарушил требование развязки. Но учтите, что клиент ничего не знает о реализации получателя. Тот факт, что получатель знает, как вызывать публичные методы для клиента, является стандартным тарифом Получатели всегда знают, что делать со своими объектами параметров. Там нет никаких зависимостей. Тот факт, что конструктор получателя принимает параметр ClientType, не имеет значения. Это может быть любой объект.

Я знаю, что это старая тема, но надеюсь, что некоторые из вас снова включатся. Не стесняйтесь разбить мое сердце, если вы видите недостаток. Это то, что мы делаем.

4 голосов
/ 20 июля 2009

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

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

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

Примечание: Тем не менее, мне было бы интересно услышать мысли о том, почему это может быть запах кода.

4 голосов
/ 20 июля 2009

Это, безусловно, спорное, но это ясно показывает два стиля мышления:

  • Проверьте, все ли в порядке, а затем действуйте соответствующим образом
  • Продолжайте в любом случае и разберитесь с этим, если случится что-то плохое

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

Вам решать, хотите ли вы, чтобы шаблон команды возвращал статус.

3 голосов
/ 26 июля 2009

Как сказано в вашем вопросе:

Если команда не выполнена, вызывающий КЛИЕНТ должен получить подтверждение статуса, и в конечном итоге развернуть соответствующая реакция.

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

С уважением,

1 голос
/ 27 июля 2009

Еще один компромисс - выставить свойство «Обработчик исключений» для конкретной команды, которая может дать сбой. Таким образом, создатель команды может обработать исключение, и вы не добавите накладные расходы на код своего клиента. Это очень полезно, когда большинство ваших команд не должно завершиться сбоем.

0 голосов
/ 21 июля 2009

В моем программном обеспечении CAD / CAM сборки, содержащие команды, ссылаются на сборки, содержащие интерфейсы и иерархию объектов, которая содержит различные элементы пользовательского интерфейса моего программного обеспечения. Это похоже на пассивное представление

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

В основном это идет

Формы реализуют IFormInterfaces и регистрируются с помощью ScreenViews в EXE

ScreenObjects реализует IScreenView и регистрируется в сборке ScreenView, а также получает команды из сборочных команд

Сборочные команды ссылаются на сборку ScreenView и модель

ScreenView Assembly немного больше, чем набор интерфейсов View и содержит реализацию приложения.

...