在大多数情况下,传统的编程智慧似乎不鼓励使用静态方法。通常,我有这些'经理',例如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);
代替。
P.S。我相信测试;我不是一个'模拟测试'的粉丝,所以除了嘲笑我还需要理由。
答案 0 :(得分:6)
避免对象工厂中的静态方法的主要原因是能够保持状态。虽然静态方法可以将其状态保持在静态字段中,但这种方法很难保存和重置工厂的状态。
此外,无法编程到工厂的接口,因为静态方法不能用作接口实现。当您需要将对象的实现透明地切换到应用程序的其余部分时,这一点变得非常重要。
最后,无论是否使用模拟,静态方法都会使测试代码变得更加困难。您的测试很难验证您的工厂的某些方法是按特定顺序调用的。
答案 1 :(得分:1)
我认为Bob叔叔在他的清洁代码书中做了很好的解释(很棒的读btw)。但无论如何,他的观点是你不应该在任何想要利用多态性的地方使用静态(我认为这正是你想要的上述情况)。
在您的情况下,您有一个UserManager。绝不是一个完整的应用程序,对吧?您可能有一些使用UserManager的更复杂的东西。让我们假设你有自己的StackOverflow版本(当然不要这样做,stackoverflow很棒,不需要竞争)。
好的,我们有一个调用UserManager.getUser()的LoginService。这是一种不可改变的依赖性(因为我们不利用多态性)。如果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)如果用户实现发生变化,可以单独进行测试。
不是嘲笑而是测试组件而不需要设置整个应用程序堆栈。
答案 2 :(得分:0)
通常不建议静态保持状态,而是支持使用依赖注入。 正如dasblinkenlight所提到的,静态方法无法实现接口 。 使用静态使得不可能有多个类的实例 如果您需要两个不同的用户管理器指向不同的数据源,您必须进行重大的重新分解。