Использую ли я здесь глобальное состояние, есть ли лучший способ сделать это? - PullRequest
1 голос
/ 10 ноября 2009

Я изменяю какой-то устаревший код. У меня есть объект, который имеет метод, скажем, doSomething (). Этот метод вызывает исключение, когда конкретное утверждение не выполняется. Но в связи с новым требованием в определенных сценариях можно не генерировать исключение и продолжить работу с методом.

Теперь я не вызываю этот метод напрямую из того места, где мне нужно игнорировать исключение. Этот doSomething () похож на метод аудита, который вызывается внутренне из многих других методов, например, method1 (), method2 () и т. Д.

В месте, где мне нужно игнорировать исключение, я вызываю method1 (), теперь я не хочу, чтобы method1 () генерировал исключение. Поэтому я изменил method1 (), чтобы получить аргумент по умолчанию method1 (ignoreException = false), и вызвал method1 (true).

Я также изменил doSomething (), чтобы получить дополнительный аргумент, а method1 просто передает ignoreException обратно в doSomething (ignoreException).

Потенциально мне нужно изменить все методы, method2, method3 и т. Д., Чтобы получить этот дополнительный аргумент.

Увидев этот код, кто-то предложил, чтобы вместо передачи этого флага я мог использовать его как переменную-член класса, а затем вызывать установщик перед вызовом method1 (). Допустим, мой объект obj, тогда я должен сделать obj.setIgnoreXXXException (истина); obj.method1 (); obj.setIgnoreXXXException (ложь);

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

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

Ответы [ 4 ]

4 голосов
/ 10 ноября 2009

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

Использование постоянного состояния для хранения временного условия даст вам две основные проблемы:

  • безопасность исключений - если функция выдает необработанное исключение, ваш код оставит флаг «игнорировать» установленным.
  • reentrancy - рекурсивный или многократный вызов функции может привести к неожиданным результатам

Вы можете сделать это исключение безопасным, используя деструктор для сброса флага:

class IgnoreException
{
public:
    explicit IgnoreException(Object &o) : object(o)
    {
        object.setIgnoreException(true);
    }
    ~IgnoreException()
    {
        object.setIgnoreException(false);
    }
private:
    Object &object;
};

void callMethodOneIgnoringException(Object &object)
{
    IgnoreException ignore(object);
    object.method1();

    // the flag is restored here, even if an exception was thrown.
}

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

1 голос
/ 10 ноября 2009

Я бы также рекомендовал использовать параметр функции вместо переменной класса.

Однако я обычно рекомендую использовать enum вместо bool:

method1(true);
// true means do throw an exception?
// do supress an exception?

enum ExceptionSuppressionType
{
  SUPPRESS_NO_EXCEPTIONS,
  SUPPRESS_ALL_EXCEPTIONS
};
method1(SUPPRESS_ALL_EXCEPTIONS);
// I'm pretty sure this will suppress the exceptions.

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

0 голосов
/ 10 ноября 2009

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

var wrapper = yourClass.WrapWithThrowOption(true);
wrapper.method();

или

var wrapper = wrapWithOption(true, method1);
wrapper();

Вы не указали, какой язык вы используете, поэтому я просто использовал псевдосинтаксис.

0 голосов
/ 10 ноября 2009

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

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