在对类进行建模时,首选的首选方法是什么:
使用其中任何一个会有什么考虑因素?
在某些情况下,我更喜欢使用一个工厂方法,如果无法构造对象,则返回null。这使代码整洁。在执行替代操作之前,我可以简单地检查返回的值是否为null,与从构造函数中抛出异常相反。 (我个人不喜欢例外)
说,我在类上有一个构造函数,它需要一个id值。构造函数使用此值从数据库填充类。如果不存在具有指定标识的记录,则构造函数将抛出RecordNotFoundException。在这种情况下,我将不得不在try..catch块中包含所有这些类的构造。
与此相反,我可以在这些类上使用静态工厂方法,如果找不到记录则返回null。
在这种情况下,哪种方法更好,构造函数或工厂方法?
答案 0 :(得分:178)
问问自己它们是什么,为什么我们拥有它们。他们都在那里创建一个对象的实例。
ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside
到目前为止没有区别。现在想象我们有各种各样的学校类型,我们想从使用ElementarySchool转到HighSchool(来自ElementarySchool或实现与ElementarySchool相同的界面ISchool)。代码更改将是:
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对象。
所以 - 对于简单的类(值对象等),构造函数很好(你不想过度使用你的应用程序)但是对于复杂的类层次结构,工厂方法是一种首选方法。
这样你就可以遵循从gang of four book“程序到界面的第一个设计原则,而不是实现”。
答案 1 :(得分:66)
您需要阅读(如果您有权访问)Effective Java 2 第1项:考虑使用静态工厂方法而不是构造函数。
静态工厂方法的优点:
静态工厂方法的缺点:
答案 2 :(得分:62)
时使用工厂方法模式
答案 3 :(得分:23)
默认情况下,构造函数应该是首选,因为它们更易于理解和编写。但是,如果您特别需要将对象的构造细节与客户端代码所理解的语义含义分离,那么最好使用工厂。
构造函数和工厂之间的区别类似于变量和指向变量的指针。还有另一层次的间接,这是一个缺点;但也有另一层灵活性,这是一个优势。因此,在做出选择时,建议您进行成本与效益分析。
答案 4 :(得分:11)
仅当您需要使用构造函数无法完成对象创建的额外控制时才使用工厂。
工厂有可能以缓存为例。
使用工厂的另一种方法是在您不知道要构造的类型的情况下。通常,您会在插件工厂方案中看到此类用法,其中每个插件必须从基类派生或实现某种接口。工厂创建派生自基类或实现接口的类的实例。
答案 5 :(得分:11)
引自“Effective Java”,第2版,第1项:考虑静态工厂方法而不是构造函数,p。 5:
“请注意,静态工厂方法与Factory Method模式不同 来自设计模式 [Gamma95,p。 107]。静态工厂方法描述于 此项目在设计模式中没有直接等效项。“
答案 6 :(得分:7)
CAD / CAM应用程序的具体示例。
使用构造函数进行切割路径。它是一系列线条和弧线,用于定义切割路径。虽然一系列的线和弧可以是不同的并且具有不同的坐标,但是通过将列表传递给构造函数可以很容易地处理它。
将使用工厂制作形状。因为虽然有一个形状类,但每个形状的设置都会有所不同,具体取决于它的形状类型。在用户进行选择之前,我们不知道我们将要初始化的形状。
答案 7 :(得分:7)
除了“有效的Java” (如另一个答案所述)外,another classic book还建议:
首选静态工厂方法(名称描述参数)而不是重载的构造函数。
例如。不要写
Complex complex = new Complex(23.0);
但是写
Complex complex = Complex.fromRealNumber(23.0);
该书甚至建议将Complex(float)
构造函数设为私有,以强制用户调用静态工厂方法。
答案 8 :(得分:5)
说,我在类上有一个构造函数,它需要一个id值。构造函数使用此值从数据库填充类。
这个过程绝对应该在构造函数之外。
构造函数不应访问数据库。
构造函数的任务和原因是使用传递给构造函数的值初始化数据成员和建立类不变。
对于其他一切,更好的方法是使用静态工厂方法,或者在更复杂的情况下使用单独的工厂或构建器类
Some constructor guide lines from Microsoft:
在构造函数中执行最少的工作。除了捕获构造函数参数之外,构造函数不应该做很多工作。任何其他处理的成本应该延迟到需要。
和
如果所需操作的语义不直接映射到新实例的构造,请考虑使用静态工厂方法而不是构造函数。
答案 9 :(得分:1)
有时您必须在创建对象时检查/计算某些值/条件。如果它可以抛出异常 - constructro是非常糟糕的方式。所以你需要做这样的事情:
var value = new Instance(1, 2).init()
public function init() {
try {
doSome()
}
catch (e) {
soAnotherSome()
}
}
所有其他计算都在init()中。但只有你作为开发人员真正了解这个init()。当然,几个月之后你就会忘记它。 但是如果你有一个工厂 - 只需要在一个方法中完成所有你需要的东西,直接调用隐藏这个init() - 所以没有问题。使用这种方法不会导致创建和内存泄漏。
有人告诉过你有关缓存的信息。这很好。但是你还必须记住Flyweight模式,它很适合与Factory方式一起使用。