当建模类时,首选的初始化方式是什么:

构造函数,或者 工厂方法

使用它们的考虑是什么?

在某些情况下,我更喜欢有一个工厂方法,如果对象不能被构造,它就返回null。这使得代码整洁。在采取替代操作之前,我可以简单地检查返回值是否不为空,而不是从构造函数抛出异常。(我个人不喜欢例外)

比如说,我在一个类上有一个构造函数,它需要一个id值。构造函数使用这个值从数据库填充类。如果指定id的记录不存在,构造函数抛出RecordNotFoundException异常。在这种情况下,我将不得不在一个try. catch块中包含所有此类类的构造。

与此相反,我可以在那些类上有一个静态工厂方法,如果没有找到记录将返回null。

在这种情况下,哪种方法更好,构造函数方法还是工厂方法?


当前回答

一个来自CAD/CAM应用的具体例子。

切割路径将使用构造函数来创建。它是一系列定义切割路径的线和弧。虽然线和弧的序列可以是不同的,并具有不同的坐标,但通过将列表传递到构造函数可以轻松处理。

一个形状将由工厂制造出来。因为虽然有一个形状类,但每个形状的设置都不同,这取决于它是什么类型的形状。我们不知道我们要初始化什么形状,直到用户做出选择。

其他回答

一个来自CAD/CAM应用的具体例子。

切割路径将使用构造函数来创建。它是一系列定义切割路径的线和弧。虽然线和弧的序列可以是不同的,并具有不同的坐标,但通过将列表传递到构造函数可以轻松处理。

一个形状将由工厂制造出来。因为虽然有一个形状类,但每个形状的设置都不同,这取决于它是什么类型的形状。我们不知道我们要初始化什么形状,直到用户做出选择。

您需要阅读Effective Java 2第1项:考虑静态工厂方法而不是构造函数。

静态工厂方法优点:

他们有名字。 它们不需要在每次调用时都创建一个新对象。 它们可以返回其返回类型的任何子类型的对象。 它们减少了创建参数化类型实例的冗长。

静态工厂方法的缺点:

当只提供静态工厂方法时,不能子类化没有公共或受保护构造函数的类。 它们很难与其他静态方法区分开来

只有在需要对对象创建进行额外控制时才使用工厂,这是构造函数无法做到的。

例如,工厂有缓存的可能性。

使用工厂的另一种方法是在不知道要构造的类型的情况下。你经常会在插件工厂场景中看到这种用法,每个插件都必须从一个基类派生出来,或者实现某种接口。工厂创建派生自基类或实现接口的类实例。

问问你自己它们是什么,为什么我们会有它们。它们都用于创建对象的实例。

ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside

到目前为止没有区别。现在想象一下,我们有各种各样的学校类型,我们想从使用ElementarySchool切换到HighSchool(它是从ElementarySchool派生出来的,或者实现了与ElementarySchool相同的isschool接口)。代码更改如下:

HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside

在接口的情况下,我们将有:

ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside

现在,如果你在多个地方都有这段代码,你可以看到使用工厂方法可能非常便宜,因为一旦你改变了工厂方法,你就完成了(如果我们使用接口的第二个例子)。

这是主要的区别和优势。当您开始处理复杂的类层次结构并希望从这样的层次结构中动态创建类的实例时,您会得到以下代码。然后,工厂方法可以接受一个参数,该参数告诉方法要实例化哪个具体实例。假设您有一个MyStudent类,您需要实例化相应的ISchool对象,以便您的学生是该学校的成员。

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

现在,您在应用程序中有了一个包含业务逻辑的地方,该业务逻辑决定为不同的IStudent对象实例化哪个ISchool对象。

因此,对于简单的类(值对象等),构造函数是很好的(你不想过度设计你的应用程序),但对于复杂的类层次结构,工厂方法是首选的方法。

这样你就遵循了四人组书中的第一个设计原则“编程到接口,而不是实现”。

比如说,我在一个类上有一个构造函数,它需要一个id值。构造函数使用这个值从数据库填充类。

这个过程肯定应该在构造函数之外。

构造函数不能访问数据库。 构造函数的任务和原因是初始化数据成员,并使用传递给构造函数的值建立类不变量。 对于其他事情,更好的方法是使用静态工厂方法,或者在更复杂的情况下使用单独的工厂或构建器类。

微软的一些构造函数指南:

在构造函数中做最少的工作。构造函数除了捕获构造函数参数之外不应该做太多工作。任何其他处理的费用应推迟到需要时。

And

如果所需操作的语义不能直接映射到新实例的构造,请考虑使用静态工厂方法而不是构造函数。