Я бы пошел тем или иным путем, но не обоими.
Если вы ожидаете, что пользователь вызовет 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);
}
}