为什么 'Manager' 类 中的 'getter' 方法不是静态的?
Why should 'getter' methods in 'Manager' classes not be static?
在大多数情况下,传统的编程智慧似乎不鼓励使用静态方法。通常,我有这些 'managers' 例如用户管理器、约会管理器 e.t.c。
总是,管理器中的一种方法是 XXX getXXX(long xxxId)
例如User getUser(long userId)
。
我真的不明白为什么这不能是静态方法。它看起来很像工厂方法(GoF 工厂模式)。
很难放弃以下便利:
User user = UserManager.getUser(id);
并使用
UserManager userManager = new UserManager();
User user = userManager.getUser(userId);
相反。
P.S。我相信测试;我不是'mock-testing'粉丝,所以除了嘲笑我还需要理由。
避免对象工厂中的静态方法的主要原因是保持状态的能力。虽然静态方法可以将它们的状态保存在静态字段中,但这种方法很难保存和重置工厂状态。
此外,无法对工厂的接口进行编程,因为静态方法不能用作接口实现。当您需要将对象的实现透明地切换到应用程序的其余部分时,这一点就变得很重要。
最后,无论是否使用模拟,静态方法都会让您更难测试您的代码。您的测试很难验证工厂的某些方法是否按特定顺序被调用。
我认为 Bob 叔叔在他的 Clean Code 一书中很好地解释了这一点(顺便说一句,读起来很棒)。但无论如何,他的观点是你不应该在任何你想利用多态性的地方使用静态(我认为这正是你想要的上述情况)。
在您的例子中,您有一个 UserManager。绝不是一个完整的应用程序,对吧?您可能有使用 UserManager 的更复杂的东西。假设您有自己的 Whosebug 版本(当然不要这样做,Whosebug 很棒,无需竞争)。
好的,我们有一个调用 UserManager.getUser() 的登录服务。这是一个不可更改的依赖关系(因为我们没有利用多态性)。如果 UserManager.getUser() 需要一个基础 SQL 数据库,那么猜猜你需要什么来 运行(或测试)LoginService....一个 SQL 数据库!
public class LoginService {
public boolean authenticate(String username, String password) {
User user = UserManager.getUser(username); // hard dependency on implementation
// other stuff
}
}
更普遍的解决方案是将可以在接口后面更改的事物抽象化。这样你就可以换出实现。 LoginService 有一个应该测试的工作,实际上不应该依赖于特定的数据库实现。
public interface UserManager {
User getUser(String id):
}
public class SQLUserManager implements UserManager {
@Override
public User getUser(String id) { // SQL stuff }
}
class LoginService {
public LoginService(UserManager userManager) {
this.userManager = userManager;
}
public boolean authenticate(String username, String password) {
User user = userManager.getUser(username);
// other stuff
}
}
现在 LoginService 可以 1) 独立于使用什么 UserManager 进行测试,并且 2) 如果用户实现发生变化,可以保持独立。
这不是模拟,而是无需设置整个应用程序堆栈即可测试您的组件。
通常不鼓励静态地保持状态以支持使用依赖注入。
正如dasblinkenlight提到的,静态方法不能实现接口
.
使用静态状态使得不可能有 class.
的多个实例
如果您需要两个不同的用户管理器指向不同的数据源,您必须进行重大重构。
在大多数情况下,传统的编程智慧似乎不鼓励使用静态方法。通常,我有这些 'managers' 例如用户管理器、约会管理器 e.t.c。
总是,管理器中的一种方法是 XXX getXXX(long xxxId)
例如User getUser(long userId)
。
我真的不明白为什么这不能是静态方法。它看起来很像工厂方法(GoF 工厂模式)。
很难放弃以下便利:
User user = UserManager.getUser(id);
并使用
UserManager userManager = new UserManager();
User user = userManager.getUser(userId);
相反。
P.S。我相信测试;我不是'mock-testing'粉丝,所以除了嘲笑我还需要理由。
避免对象工厂中的静态方法的主要原因是保持状态的能力。虽然静态方法可以将它们的状态保存在静态字段中,但这种方法很难保存和重置工厂状态。
此外,无法对工厂的接口进行编程,因为静态方法不能用作接口实现。当您需要将对象的实现透明地切换到应用程序的其余部分时,这一点就变得很重要。
最后,无论是否使用模拟,静态方法都会让您更难测试您的代码。您的测试很难验证工厂的某些方法是否按特定顺序被调用。
我认为 Bob 叔叔在他的 Clean Code 一书中很好地解释了这一点(顺便说一句,读起来很棒)。但无论如何,他的观点是你不应该在任何你想利用多态性的地方使用静态(我认为这正是你想要的上述情况)。
在您的例子中,您有一个 UserManager。绝不是一个完整的应用程序,对吧?您可能有使用 UserManager 的更复杂的东西。假设您有自己的 Whosebug 版本(当然不要这样做,Whosebug 很棒,无需竞争)。
好的,我们有一个调用 UserManager.getUser() 的登录服务。这是一个不可更改的依赖关系(因为我们没有利用多态性)。如果 UserManager.getUser() 需要一个基础 SQL 数据库,那么猜猜你需要什么来 运行(或测试)LoginService....一个 SQL 数据库!
public class LoginService {
public boolean authenticate(String username, String password) {
User user = UserManager.getUser(username); // hard dependency on implementation
// other stuff
}
}
更普遍的解决方案是将可以在接口后面更改的事物抽象化。这样你就可以换出实现。 LoginService 有一个应该测试的工作,实际上不应该依赖于特定的数据库实现。
public interface UserManager {
User getUser(String id):
}
public class SQLUserManager implements UserManager {
@Override
public User getUser(String id) { // SQL stuff }
}
class LoginService {
public LoginService(UserManager userManager) {
this.userManager = userManager;
}
public boolean authenticate(String username, String password) {
User user = userManager.getUser(username);
// other stuff
}
}
现在 LoginService 可以 1) 独立于使用什么 UserManager 进行测试,并且 2) 如果用户实现发生变化,可以保持独立。
这不是模拟,而是无需设置整个应用程序堆栈即可测试您的组件。
通常不鼓励静态地保持状态以支持使用依赖注入。
正如dasblinkenlight提到的,静态方法不能实现接口
.
使用静态状态使得不可能有 class.
的多个实例
如果您需要两个不同的用户管理器指向不同的数据源,您必须进行重大重构。