假设出于某种原因,所有对象都是通过$ obj = CLASS :: getInstance()创建的。然后,我们使用setter注入依赖项,并使用$ obj-> initInstance();开始初始化。如果我们根本不使用构造函数,是否有无法解决的实际麻烦或情况?
ps用这种方法创建对象的原因是,我们可以根据一些规则替换getInstance()中的类。
我在用PHP工作,如果那件事
假设出于某种原因,所有对象都是通过$ obj = CLASS :: getInstance()创建的。然后,我们使用setter注入依赖项,并使用$ obj-> initInstance();开始初始化。如果我们根本不使用构造函数,是否有无法解决的实际麻烦或情况?
ps用这种方法创建对象的原因是,我们可以根据一些规则替换getInstance()中的类。
我在用PHP工作,如果那件事
Answers:
我想说,这严重地阻碍了您的设计空间。
构造函数是初始化和验证传入参数的好地方。如果您不能再使用它们了,则初始化,状态处理(或明确拒绝“断开”对象的构造函数)将变得更加困难,部分无法实现。
例如,如果每个Foo
对象都需要a,Frobnicator
那么它可能会在其构造函数中检入Frobnicator
非null值。如果带走了构造函数,则检查起来会更加困难。您是否会检查使用地点的每一点?在一个init()
方法中(有效地外部化构造方法)?永远不要检查并希望最好?
虽然你可能可能仍然实现一切(毕竟,你仍然图灵完备),有些东西会很多更难做。
HttpClient
则它将检查该参数是否为非null)。如果不满足这些约束,则可能引发异常。使用构造和设置值方法实际上是不可能的。
init()
,这是完全可能的,尽管这只会增加更多的维护负担。
构造函数的2个优点:
我可以避免使用构造函数,而是对所有事物使用设置方法,但是如Joachim Sauer所建议的那样,强制属性又如何呢?对于构造函数,对象拥有自己的构造逻辑,以确保不存在此类的无效实例。
如果创建的实例Foo
需要设置3个属性,则构造函数可以引用所有3个属性,并对其进行验证,如果它们无效则抛出异常。
通过仅依靠安装者,负担是对象的消费者要正确地构建它。有效的属性可能有不同的组合。
例如,每一个Foo
实例都需要任一实例Bar
作为属性bar
或实例BarFinder
作为属性barFinder
。它可以使用任何一个。您可以为每个有效的参数集创建一个构造函数,并以此方式执行约定。
对象的逻辑和语义存在于对象本身内。这是很好的封装。
是的,您可以没有构造函数。
当然,您可能会得到很多重复的样板代码。而且,如果您的应用程序规模不限,当样板代码在整个应用程序中使用不一致时,您可能会花费大量时间来尝试查找问题的根源。
但是不,您不必严格“需要”自己的构造函数。当然,您也不严格“需要”类和对象。
现在,如果您的目标是使用某种工厂模式进行对象创建,那么在初始化对象时,这与使用构造函数并不相互排斥。
$ obj = CLASS :: getInstance()。然后,我们使用setter注入依赖项,并使用$ obj-> initInstance();开始初始化。
我认为您正在使这一过程变得更加困难。我们可以通过构造函数很好地注入依赖项-如果您有很多依赖项,则只需使用类似字典的结构即可指定要使用的依赖项:
$obj = new CLASS(array(
'Frobnicator' => (),
'Foonicator' => (),
));
在构造函数中,您可以像这样确保一致性:
if (!array_key_exists('Frobnicator', $args)) {
throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
$args['Foonicator'] = new DefaultFoonicator();
}
$args
然后可以根据需要用于设置私有成员。
当像这样完全在构造函数中完成操作时,就不会有中间状态$obj
存在但没有初始化,就像问题中描述的系统那样。最好避免这种中间状态,因为您不能保证总是正确使用该对象。
我实际上在考虑类似的事情。
我问的问题是“构造函数是做什么的,是否有可能做不同的事情?” 我得出了这些结论:
它确保初始化某些属性。通过接受它们作为参数并进行设置。但这很容易由编译器实施。通过简单地将字段或属性注释为“必需”,编译器将在实例创建期间检查所有设置是否正确。创建实例的调用可能是相同的,只是不会有任何构造方法。
确保属性有效。这可以通过断言条件轻松实现。同样,您只需使用正确的条件注释属性。一些语言已经做到了。
一些更复杂的构造逻辑。现代模式不建议在构造函数中执行此操作,而是建议使用专门的工厂方法或类。因此,在这种情况下,构造函数的使用最少。
因此,请回答您的问题:是的,我相信这是可能的。但这需要对语言设计进行一些重大更改。
我只是注意到我的答案很不错。
是的,几乎不需要使用构造函数就可以做所有事情,但这显然浪费了面向对象编程语言的好处。
在现代语言中(我将在此处讨论我在其中编程的C#),您可以限制只能在构造函数中运行的部分代码。有了它,您可以避免笨拙的错误。这样的事情之一是只读修饰符:
public class A {
readonly string rostring;
public A(string arg) {
rostring = arg;
}
public static A CreateInstance(string arg) {
var result = new A();
A.rostring = arg; // < because of this the code won't compile!
return result;
}
}
如Joachim Sauer所建议,而不是使用Factory
设计模式阅读Dependency Injection
。我建议阅读Mark Seemann撰写的.NET中的依赖注入。
为了平衡其他一些答案,声称:
构造函数使您有机会将对象的所有成员变量设置为有效状态……您永远不会有无效的对象,这将使您免于遇到许多错误。
和
对于构造函数,对象拥有自己的构造逻辑,以确保不存在此类的无效实例。
这些陈述有时暗示着以下假设:
如果一个类具有一个构造函数,该构造函数在退出时已将对象置于有效状态,并且该类的任何方法都不会使该状态发生变化以使其无效,则该类之外的代码无法检测到对象该类的状态处于无效状态。
但这不是真的。大多数语言都没有禁止构造函数将this
(self
或其他语言称为)传递给外部代码的规则。这样的构造函数完全遵守上述规则,但是有将半构造对象暴露给外部代码的风险。这是次要的一点,但很容易被忽略。