在大多数情况下,传统的编程智慧似乎阻碍了静态方法的使用。通常,我有这些“经理”,例如UserManager,AppointmentManager e.t.c。管理器中的方法之一总是XXX getXXX(long xxxId),例如User getUser(long userId)。我真的不明白为什么这不能是一个静态的方法。它看起来非常像一个工厂方法( la GoF工厂模式)。很难放弃以下便利:
User user = UserManager.getUser(id);
并使用
UserManager userManager = new UserManager(); User user = userManager.getUser(userId);
而不是。
我相信测试,我只是不是一个“模拟测试”的粉丝,所以我需要的理由除了嘲笑。
发布于 2016-06-28 00:09:28
避免对象工厂中的静态方法的主要原因是保持状态的能力。虽然静态方法可以将它们的状态保持在静态字段中,但是这种方法很难保存和重置工厂的状态。
此外,不可能对工厂的接口进行编程,因为静态方法不能用作接口实现。当您需要透明地将对象的实现切换到应用程序的其他部分时,这一点变得非常重要。
最后,静态方法增加了测试代码的难度,不管是否使用了模拟。您的测试将很难验证您的工厂的某些方法是按特定顺序调用的。
发布于 2016-06-28 00:45:15
我认为Bob叔叔在他的清洁代码书中解释这个问题做得很好(顺便说一句,很棒)。但是无论如何,他的观点是,您不应该在任何想要利用多态性的地方使用静力学(我认为这正是您在上述情况下所想要的)。
在您的例子中,您有一个UserManager。绝不是一个完整的申请,对吧?您可能有一些使用UserManager的更复杂的东西。假设您有自己的StackOverflow版本(当然,不要这样做,堆栈溢出是很棒的,不需要竞争)。
好的,我们有一个调用LoginService ()的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可以独立于使用什么UserManager进行测试,如果用户实现发生变化,则可以单独进行测试。
这不是模拟,而是测试您的组件,而不需要设置整个应用程序堆栈。
发布于 2016-06-28 00:45:24
通常不鼓励静态地保持状态,而倾向于使用依赖注入。如前所述,静态方法无法实现接口。使用静态使类不可能有多个实例。
如果您需要两个不同的用户管理器,指向不同的数据源,则必须进行主要的重构。
https://stackoverflow.com/questions/38064993
复制相似问题