我最近开始研究一个Java项目,该项目已经拥有一个由3个月以上的团队开发的相当大的代码库。我注意到在很多地方,一些对象直接在客户端对象的构造函数中实例化,而不是使用依赖注入。我想将对象构造重构为工厂并使用一些注入框架。
我创建了一个工厂,基本上是一个工作new <type(some params here)>
的工作室。这里没什么好看的 - 没有单身,没有静态的工厂模式。只是一个newInstance()
方法,它返回一个新的依赖实例。
在代码中显示内容:
class A { A() {
B bobj = new B(); // A and B are coupled directly
}
}
我想将此重构为:
BFactory {
newInstance() { return new B(); // return B implementation }
}
class A {
A(BFactory factory){
B bobj = factory.newInstance(); // A does not know about B impl
}
}
我的论点是不应该在代码中的任何地方创建对象,除非在为此目的的工厂中。这促进松耦合,否则你紧紧地耦合这两种类型。一位高级成员(我试图重构的代码的作者)认为,一个班轮工厂是一个过于复杂的设计。
是否有关于此问题的模式的权威建议/参考?可以用什么来决定哪种方法更好,为什么更好呢?
答案 0 :(得分:1)
一位高级会员(我试图重构的代码的作者)认为,一家班轮工厂是一个过于复杂的设计。
这看起来像你问题的关键,而不是你是否应该重构代码,所以让我们回答它而不是偏离实际的问题。如果我们考虑您在代码中提供的示例,我同意您的同事。您不应该为要注入的每个依赖项创建工厂类。你试图实现的目标没有任何问题,但是你试图实现它的方式是一种过度杀伤。
您要么依赖于Factory
类的层次结构,这些类知道如何创建每个依赖项,要么依赖于实际的类本身,并且有一个Container
可以将对象连接在一起。
选项1:取决于常见工厂
class A {
B bobj;
C cobj;
A(Factory factory){
bobj = factory.createB();
cobj = factory.createC();
}
}
选项2:直接依赖于依赖
class A {
B bobj;
C cobj;
A(A a,B b) {
this.bobj = b;
this.cobj = c
}
}
然后,您可以创建一个知道如何将对象连接在一起的Container
类:
class Container {
public static B createB() {
return new BImpl();
}
public static C createC() {
return new CImpl();
}
public static A createA() {
return newAImpl(createB(),createC());
}
}
上面提到的例子太基础了。在现实世界中,您将主要拥有更复杂的依赖关系图。这就是DI框架派上用场而不是重新发明轮子的地方。如果您的最终目标是开始使用DI框架,那么您可以使用选项2,因为DI框架通过向客户端提供依赖关系来实现控制的反转,而不是客户端代码要求它们。
答案 1 :(得分:0)
你的基础点是完全有效的,直接在构造函数中实例化对象通常不是一个好主意(它们可能是该规则的有效例外)。
如果你这样做:
class Car {
private Engine engine;
public Car() {
engine = new Engine();
}
}
如果没有发动机,您将很难测试汽车。您必须使用反射才能通过模拟交换实例。
如果您执行以下操作
class Car {
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
}
使用假引擎测试汽车非常容易,替换引擎的实现或更改引擎的构造方式(例如,向构造函数添加更多参数)。
但是你绝对应该使用已建立的依赖注入框架而不是编写自己的工厂。如果你传入一个“普通工厂”,Chetan建议你最终隐藏你的依赖。
可以在此处找到使用依赖注入获得更多动力的良好资源: https://github.com/google/guice/wiki/Motivation或 https://youtu.be/acjvKJiOvXw(非常好的谈话,应该值得你的时间)。