Java对象创建模式与设计
我最近开始从事一个Java项目,该项目已经有一个相当大的代码库,由一个团队在3个月内开发。我注意到,在许多地方,一些对象直接在客户机对象的构造函数中实例化,而不是使用依赖项注入。我想把对象结构重构成一个工厂,并使用一些注入框架 我创建了一个工厂,它本质上是一个doingJava对象创建模式与设计,java,object,factory,Java,Object,Factory,我最近开始从事一个Java项目,该项目已经有一个相当大的代码库,由一个团队在3个月内开发。我注意到,在许多地方,一些对象直接在客户机对象的构造函数中实例化,而不是使用依赖项注入。我想把对象结构重构成一个工厂,并使用一些注入框架 我创建了一个工厂,它本质上是一个doingnew的一行程序。这里没有什么花哨的东西——没有单件,没有静态工厂模式。只有一个newInstance()方法,它返回依赖项的新实例 要在代码中显示某些内容: class A { A() { B bobj =
new
的一行程序。这里没有什么花哨的东西——没有单件,没有静态工厂模式。只有一个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
}
}
我的论点是,不应该在代码中的任何地方创建对象,除非是在用于此目的的工厂中。这会促进松散耦合,否则会将这两种类型紧密耦合。一位资深成员(我试图重构的代码的作者)认为单行程序工厂的设计过于复杂
是否有权威性的建议/参考来解决这个问题?可以用来决定哪种方法更好,为什么更好
一位资深成员(我试图重构的代码的作者)认为单行程序工厂的设计过于复杂
这似乎是您问题的关键,而不是您是否应该重构代码。因此,让我们回答它,而不是偏离实际问题。如果我们考虑你在代码中出现的例子,我同意你的同事。您不应该为每个要注入的依赖项创建工厂类。你试图实现的目标没有错,但是你试图实现它的方式是一种过度的杀伤力
您要么依赖于知道如何创建每个依赖项的工厂
类的层次结构,要么依赖于实际的类本身,拥有一个容器
,可以将对象连接在一起
选项1:依赖于一个通用工厂
class A {
B bobj;
C cobj;
A(Factory factory){
bobj = factory.createB();
cobj = factory.createC();
}
}
class A {
B bobj;
C cobj;
A(A a,B b) {
this.bobj = b;
this.cobj = c
}
}
选项2:直接依赖依赖依赖关系
class A {
B bobj;
C cobj;
A(Factory factory){
bobj = factory.createB();
cobj = factory.createC();
}
}
class A {
B bobj;
C cobj;
A(A a,B b) {
this.bobj = b;
this.cobj = c
}
}
然后可以创建一个容器类,该类知道如何将对象连接在一起:
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框架通过向其客户机提供依赖项而不是客户机代码来实现控制反转。您的基本点是完全有效的,直接在构造函数中实例化对象通常不是一个好主意(它们可能是该规则的有效例外)
如果您这样做:
class Car {
private Engine engine;
public Car() {
engine = new Engine();
}
}
在没有引擎的情况下,你将很难测试汽车。你必须使用反射来通过模拟来交换实例
如果您改为执行以下操作
class Car {
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
}
使用假发动机测试汽车、更换发动机的实现或改变发动机的构造方式(例如,向构造器添加更多参数)非常容易
但是你绝对应该使用一个已经建立的依赖注入框架,而不是编写你自己的工厂。如果你像Chetan建议的那样传入一个“公共工厂”,你最终会隐藏你的依赖
在这里可以找到使用依赖项注入获得更多动力的好资源:
或
(非常好的谈话,应该值得你花时间)。我的论点是“如果它没有损坏,就不要修复它。”使用工厂方法获得的耦合损失不值得花时间或冒风险(bug)它需要触及你代码库的每一部分。现在就开始吧。你的帖子能举一个简短的例子来说明你在说什么吗?你的描述可以用几种不同的方式来解释。我会稍微将@markspace的注释改为“等到它坏了再修复它”。如果您需要使用工厂方法进行更简单、更干净的更改,请重构该类的构造。如果某个类正在运行且不需要更改,请别管它。我认为这个问题不值得以主要基于意见的方式来结束。OP问他做某事的方式是否正确正确的方法。我不认为答案取决于你的情况。只有在你需要的时候才使用松耦合,如果一个工厂可以创建该接口/类的多个实现,那么你应该使用一个工厂来创建它们,如果它是一个对象,那么不要通过向它添加工厂来让生活变得复杂。为什么不同时使用这两种类型的构造函数呢从这两个类中,一个是提供引擎的默认类,而另一个则允许您注入引擎。@AlexC以及当其中一个客户端的DefaultEngine
不再是以前的DefaultEngine
时会发生什么?理想情况下,工厂
应该注入一个作为默认引擎的引擎f客户不知道他们想要什么引擎。对于一个客户,Engine1
可能是默认引擎,而对于另一个客户,Engine2
应该是Car
真的在乎吗?答案是否定的,因为这违背了控制反转的目的。很多假设。你编了一个用例,现在正在编约束ts和回答您自己的问题。如果包含默认值的IoC配置不再是以前的配置,该怎么办?代码可能会过时,配置文件也可能会过时。请再次阅读我的答案,您可以同时拥有默认行为和IoC,您的重点是什么?@AlexC我认为您完全没有理解我的意思。让我重新措辞。默认引擎有什么不同t来自任何其他发动机
。为什么汽车
应该知道它的默认发动机是什么?汽车不应该是汽车