为了遵循实用的编程原则,我试图根据“告诉,不要问”原则来决定如何处理用户密码更改。
我有一个用户对象,其密码每 30 天过期一次。如果密码过期,我需要能够显示密码过期/更改密码视图。询问对象密码是否过期(它的状态)然后选择显示哪个视图似乎违反了原则。
处理这种情况的最佳方法是什么?
为了遵循实用的编程原则,我试图根据“告诉,不要问”原则来决定如何处理用户密码更改。
我有一个用户对象,其密码每 30 天过期一次。如果密码过期,我需要能够显示密码过期/更改密码视图。询问对象密码是否过期(它的状态)然后选择显示哪个视图似乎违反了原则。
处理这种情况的最佳方法是什么?
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()
告诉,不问,没有例外,它遵守得墨忒耳法则
当密码通过身份验证时,您可以从用户对象中抛出 PasswordExpired 异常,或者您首先在用户上调用的任何函数。
您应该考虑让用户对象有一个提供布尔值的 Validate() 方法(就像会员提供者合同一样),或者考虑让 Validate() 方法返回某种指示验证结果的枚举(OK,INVALID_PASSWORD , EXPIRED_PASSWORD 等)。
有很多选择——如果密码过期,抛出异常不应该是其中之一。这是一种糟糕的形式,并且由于运行时必须展开堆栈,因此也会影响性能。
我个人不喜欢编写 arround 返回值/Enum
类型。您拥有的返回类型越多,您必须测试/使用的路径就越多。此外,使用异常来控制流程是一种不好的做法(除非您真的找不到任何其他选择 - 但通常有更好的选择)。
过期的密码对我来说并不是什么特别的事情。毕竟它是一个有效的状态(否则你会做一些反对密码过期的事情)
我尽量保持简单,并返回 abool
或类似 a 的东西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();
解决此问题的一种方法是像这样的 OO 建模:
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 进行身份验证,因为它拥有执行此操作所需的一切。