Принцип «Говори, не спрашивай» и срок действия пароля

Пытаясь придерживаться прагматических принципов программирования, я пытаюсь решить, как обрабатывать изменения пароля пользователя, основываясь на принципе «Говори, не спрашивай».

У меня есть пользовательский объект, срок действия пароля которого истекает каждые 30 дней. Мне нужно иметь возможность показывать окно с истекшим сроком действия/изменить пароль, если срок действия пароля истек. Запрашивать объект, истек ли срок действия пароля (его состояние), а затем выбирать, какое представление отображать, кажется нарушением принципа.

Каков наилучший способ справиться с этой ситуацией?


person Hupperware    schedule 26.03.2012    source источник
comment
Истекающие пароли - глупая вещь. Люди просто склонны использовать дрянные, поскольку им просто удалось запомнить свой сложный, когда им нужно будет снова его изменить.   -  person ThiefMaster    schedule 27.03.2012
comment
Соответствие PCI не согласно с вами   -  person Hupperware    schedule 27.03.2012
comment
@Hupperware, который фигурирует :-p   -  person jnm2    schedule 15.01.2016


Ответы (5)


login
   model.validate();
   return model.show(self);

passwordExpired()
  return View("ChangePassword")

loginSuccess()
  return View("default")

class User
  show(aController)
      if passwordExpired
          return aContoller.passwordExpired()
     else return aContoller.loginSuccess()

Скажи, не спрашивай, без исключений и подчиняется закону Деметры

person Hupperware    schedule 26.03.2012

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

person GavinCattell    schedule 26.03.2012
comment
Но это нарушает другой принцип — не управляйте потоком с помощью исключений. Срок действия пароля не является исключительной ситуацией. - person NOtherDev; 26.03.2012
comment
Вы не должны использовать исключения для потока управления — они дороги. - person David Hoerster; 26.03.2012
comment
Хорошие моменты, спасибо. Однако, если вы хотите придерживаться принципа «Говори, не спрашивай», как вы можете справиться с этим без исключений? Вернуть состояние из функции? - person GavinCattell; 26.03.2012
comment
Разве это не исключительно? Обычно вы не ожидаете, что срок действия пароля истечет в вашем обычном потоке программы. - person DanDan; 26.03.2012
comment
Нет. Это то, с чем вы столкнетесь при работе с приложением, используя правильные данные и правильный поток. Исключение означает, что происходит что-то неправильное, а не просто что-то за пределами счастливой траектории. - person NOtherDev; 27.03.2012
comment
Но если срок действия пароля может истечь в любой момент, как это можно предсказать? Вы же не хотите проверять возвращаемые значения каждый раз, когда что-то спрашиваете у сервера! - person DanDan; 27.03.2012
comment
Ну, как всегда, это зависит. Может, нам не прерывать работу нашего пользователя посреди чего-то, а проверять правильность пароля только при входе в систему? - person NOtherDev; 27.03.2012

Вы должны рассмотреть возможность того, чтобы пользовательский объект имел метод Validate(), который предоставляет логическое значение (как это делает контракт поставщика членства), или рассмотреть возможность того, чтобы метод Validate() возвращал какое-то перечисление, указывающее результат проверки (ОК, INVALID_PASSWORD , EXPIRED_PASSWORD и т. д.).

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

person David Hoerster    schedule 26.03.2012
comment
Я никогда не понимал этого аргумента... стек довольно хорошо раскручивается. Это то, что он должен делать. - person DanDan; 27.03.2012
comment
Сделайте тест. Напишите консольное приложение, которое создает исключение и записывает выходные данные в консоль. Запустите его в режиме отладки. Обратите внимание, сколько времени требуется для создания и отображения вашего исключения. Когда вы выбрасываете исключение, происходит много всего — это дорого. Я также понимаю, что отладка также медленнее, чем работа в режиме выпуска, но все же многое происходит, когда вы выдаете исключение. - person David Hoerster; 27.03.2012
comment
Это нормально... Я обработаю свой объект исключения, когда он появится, я буду в лучшем месте для решения проблемы, потому что он полон полезной информации. Меня не волнует, если на постройку уйдет несколько дополнительных наносекунд. - person DanDan; 27.03.2012
comment
Кого волнует, сколько времени это займет в режиме отладки. Вы не используете исключения в жестком цикле, когда важна производительность, но если вас беспокоит стоимость одного исключения в такой ситуации, как неправильный вход в систему, вы страдаете от синдрома преждевременной оптимизации, преодолейте это, исключения в порядке, пока профилировщик не скажет, что это не так. Уведомления об исключениях вызовов Lisp и Smalltalk и условия. Исключение — это подкласс уведомлений, т. е. они полезны для потока управления в не исключительных ситуациях. Неправильно избегать исключений из соображений производительности, если об этом не говорит профайлер. - person Ramon Leon; 27.03.2012
comment
Неплохой тон — выбрасывать исключение по истечении срока действия пароля, что, кстати, является исключительным условием, т. е. не является основным путем к успеху. Перехватывайте InvalidPassword или ExpiredPassword и отображайте соответствующее представление. Здесь исключения работают хорошо, они элегантны и не снижают производительности. Время, необходимое для создания одного исключения в режиме выпуска, просто не стоит учитывать, когда потребуется несколько порядков, чтобы попасть в базу данных и проверить пароль. - person Ramon Leon; 27.03.2012
comment
Это также будет медленнее в режиме выпуска, например, при сравнении с нулевым значением или проверке возвращаемого значения. Суть в том, что исключения должны генерироваться в исключительных случаях, а не использоваться для управления потоком логики. Как правило, они используются для обработки тех ситуаций, которые находятся вне вашего контроля (файл не найден, база данных недоступна и т. д.), а не является частью логики, которую следует ожидать (истек срок действия пароля, достигнут конец файла и т. д.). ). Я думаю, что влияние будет более заметным, чем наносекунды, независимо от режима выполнения. - person David Hoerster; 27.03.2012

Мне лично не нравится программировать возвращаемые значения / Enum типы. Чем больше у вас типов возврата, тем больше путей вам нужно протестировать/поработать. Кроме того, использование исключений для управления потоком является плохой практикой (если вы действительно не можете найти другой вариант, но обычно есть лучший).

Просроченный пароль на самом деле не является исключительным для меня. В конце концов, это действительное состояние (иначе вы бы сделали что-то против истечения срока действия паролей вообще)

Я стараюсь не усложнять и либо возвращаю bool, либо что-то вроде Func<T>, которое может быть вызвано вызывающим кодом напрямую.

Вероятно, что-то вроде этого:

public class User
    {
        private DateTime _lastChangeDate;
        public Action Validate()
        {
            if (_lastChangeDate >= DateTime.Now.AddDays(-30))
            {
                return new Action(() => this.Login());
            }
            else
            {
                return new Action(() => this.ChangePassword());
            }
        }
        private void Login()
        {
            Console.WriteLine("Login");
        }
        private void ChangePassword()
        {
            Console.WriteLine("Change Password");
        }
    }

На стороне вызывающего абонента:

user.Validate().Invoke();
person Alex    schedule 26.03.2012

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

public class Login {

private String userName;
private String password;
private Date expirationDate;    

public void authenticate(String password) {
    if (this.password.equals(password) {
        redirectoToMainOrEditPage();
    } else {
        redirectToFailPage();
    }
}

private void redirectToMainOrEditPage() {
    Date today = new Date();

    if (today.before(expirationDate)) {
        redirectToMainPage();
    } else {
        redirectToEditPage();
    }
}

private void redirectToMainPage() {
    ...
}

private void redirectToEditPage() {
    ...
}

private void redirectToFailPage() {
    ...
}

public void changePassword(String newPassword) {
    ...
}

public void changeExpirationDate(Date newDate) {
    ...
}
}

Таким образом, вы ничего не спрашиваете у других объектов домена, а указываете Login для аутентификации, так как у него есть все необходимое для этого.

person user2014029    schedule 26.01.2013