为什么 '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.
的多个实例 如果您需要两个不同的用户管理器指向不同的数据源,您必须进行重大重构。