ICommand - Должен ли я вызвать CanExecute в Execute? - PullRequest
15 голосов
/ 08 августа 2011

Учитывая, что System.Windows.Input.ICommand как 2 основных метода:

interface ICommand {
  void Execute(object parameters);
  bool CanExecute(object parameters);
  ...
}

Я ожидаю, что CanExecute (...) будет вызван в платформах, поддерживаемых Командой, до вызова Execute (...) .

Внутри моей реализации команды, однако, есть ли причина добавить вызов CanExecute (...) в мою реализацию Execute (...) ?

например:.

public void Execute(object parameters){
  if(!CanExecute(parameters)) throw new ApplicationException("...");
  /** Execute implementation **/
}

Это становится актуальным в моем тестировании, так как я могу макетировать некоторые интерфейсы для поддержки CanExecute , и при тестировании Execute придется выполнять те же самые макеты.

Есть какие-нибудь мысли по поводу этого дизайна?

Ответы [ 7 ]

7 голосов
/ 08 августа 2011

Программисты, как известно, ленивы, и они будут звонить Execute, не вызывая CanExecute сначала.

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

Я вызываю CanExecute немедленно из метода Execute, чтобы проверить состояние объекта. Это помогает уменьшить дублирование логики и обеспечивает использование метода CanExecute (зачем делать все возможное, чтобы они могли вызывать метод и не беспокоить его принудительное применение?). Я не вижу проблемы с вызовом CanExecute много раз, потому что в любом случае это должна быть быстрая операция.

Однако я всегда документирую результаты вызова метода Execute, если метод CanExecute возвращает false, так что потребитель знает о последствиях.

3 голосов
/ 08 августа 2011

Я бы пошел тем или иным путем, но не обоими.

Если вы ожидаете, что пользователь вызовет CanExecute, не вызывайте его в Execute.Вы уже установили это ожидание в своем интерфейсе, и теперь у вас есть контракт со всеми разработчиками, который подразумевает такого рода взаимодействие с ICommand.

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

Пример:

interface Command {
    void Execute(object parameters);    
}

class CommandImpl: ICommand {
    public void Execute(object parameters){
        if(!CanExecute(parameters)) throw new ApplicationException("...");
        /** Execute implementation **/
    }
    private bool CanExecute(object parameters){
        //do your check here
    }
}

Таким образом, вы заключаете контракт (интерфейс) ясен и лаконичен, и вас не смущает вопрос о том, вызывается ли CanExecute дважды.

Однако, если вы на самом деле застряли с интерфейсом, потому что не управляете им, другое решениеможно сохранить результаты и проверить это так:

interface Command {
    void Execute(object parameters);    
    bool CanExecute(object parameters);
}

class CommandImpl: ICommand {

    private IDictionary<object, bool> ParametersChecked {get; set;}

    public void Execute(object parameters){
        if(!CanExecute(parameters)) throw new ApplicationException("...");
        /** Execute implementation **/
    }

    public bool CanExecute(object parameters){
        if (ParametersChecked.ContainsKey(parameters))
            return ParametersChecked[parameters];
        var result = ... // your check here

        //method to check the store and add or replace if necessary
        AddResultsToParametersChecked(parameters, result); 
    }
}
2 голосов
/ 08 августа 2011

Я не был бы столь же оптимистичен, как другие, по поводу добавления вызова к CanExecute в Execute реализацию. Что если выполнение вашего CanExecute займет очень много времени? Это будет означать, что в реальной жизни ваш пользователь будет ждать в два раза дольше - один раз, когда CanExecute вызывается средой, а затем, когда он вызывается вами.

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

1 голос
/ 08 августа 2011

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

1 голос
/ 08 августа 2011

Вероятно, не будет никакого вреда для вызова CanExecute() в пределах Execute(). Обычно Execute() предотвращается, если CanExecute() возвращает false, поскольку они обычно связаны с пользовательским интерфейсом и не вызываются в вашем собственном коде. Однако ничто не заставляет кого-либо вручную проверять CanExecute() перед вызовом Execute(), так что неплохо было бы встроить эту проверку.

Некоторые инфраструктуры MVVM действительно проверяют это перед вызовом Execute(). Они не бросают исключения, хотя. Они просто не звонят Execute(), поэтому вы можете не захотеть создавать исключение самостоятельно.

Вы можете рассмотреть вопрос о том, будете ли вы генерировать исключение, если CanExecute() вернет false на том, что будет делать Execute(). Если он будет выполнять то, что, как ожидается, завершится чем-либо после вызова Execute(), тогда бросание имеет смысл. Если эффект от вызова Execute() не так уж важен, то, возможно, более уместным будет возвращение без звука.

0 голосов
/ 21 апреля 2016

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

public void SafeExecute(object parameter) {
    if (CanExecute(parameter)) {
        Execute(parameter);
    }
}

Конечно, это не помешает непосредственному вызову Execute(), но, по крайней мере, вы следуете принципу СУХОЙ и не вызываете его дважды при каждом выполнении.

То же самое может быть реализовано для исключения CanExecute(), возвращающего false.

0 голосов
/ 24 октября 2014

CanExecute - это простой способ MSFT (я бы сказал, взломать) для интеграции команды с элементами управления пользовательского интерфейса, такими как кнопка.Если мы связываем команду с кнопкой, FWK может вызвать CanExecute и включить / отключить кнопку.В этом смысле мы не должны вызывать CanExcute из нашего метода Execute.

Но если мы подумаем с точки зрения ООП / многоразового использования, мы увидим, что ICommand может использоваться и для не-пользовательского интерфейса, например, для кода оркестрации / автоматизации, вызывающего мою Команду.В этом сценарии нет необходимости, чтобы автоматизация вызывала CanExecute перед вызовом my Execute (). Поэтому, если мы хотим, чтобы наша реализация ICommand работала согласованно, нам нужно вызвать CanExecute ().Да, есть проблема с производительностью, и мы должны с ней справиться.

На мой взгляд, .Net framework нарушил провайдера в этой команде, например, как его нарушали для ASP.Net Membership provider.Пожалуйста, исправьте, еслиЯ не прав

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