构造函数与工厂方法

发布于 2024-07-14 08:53:05 字数 1431 浏览 4 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

眼中杀气 2024-07-21 08:53:05

问问自己它们是什么以及为什么我们有它们。 它们都是为了创建对象的实例。

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 对象。

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

这样,您就可以遵循四本书中的第一个设计原则“编程一个接口,而不是一个实现”。

Ask yourself what they are and why do we have them. They both are there to create instance of an object.

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

No difference so far. Now imagine that we have various school types and we want to switch from using ElementarySchool to HighSchool (which is derived from an ElementarySchool or implements the same interface ISchool as the ElementarySchool). The code change would be:

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

In case of an interface we would have:

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

Now if you have this code in multiple places you can see that using factory method might be pretty cheap because once you change the factory method you are done (if we use the second example with interfaces).

And this is the main difference and advantage. When you start dealing with a complex class hierarchies and you want to dynamically create an instance of a class from such a hierarchy you get the following code. Factory methods might then take a parameter that tells the method what concrete instance to instantiate. Let's say you have a MyStudent class and you need to instantiate corresponding ISchool object so that your student is a member of that school.

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

Now you have one place in your app that contains business logic that determines what ISchool object to instantiate for different IStudent objects.

So - for simple classes (value objects, etc.) constructor is just fine (you don't want to overengineer your application) but for complex class hierarchies factory method is a preferred way.

This way you follow the first design principle from the gang of four book "Program to an interface, not an implementation".

﹏半生如梦愿梦如真 2024-07-21 08:53:05

您需要阅读(如果您有权访问)Effective Java 2 第 1 项:考虑静态工厂方法而不是构造函数

静态工厂方法的优点:

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

静态工厂方法的缺点:

  1. 当仅提供静态工厂方法时,没有公共或受保护构造函数的类不能被子类化。
  2. 它们与其他静态方法不容易区分

You need to read (if you have access to) Effective Java 2 Item 1: Consider static factory methods instead of constructors.

Static factory methods advantages:

  1. They have names.
  2. They are not required to create a new object each time they are invoked.
  3. They can return an object of any subtype of their return type.
  4. They reduce verbosity of creating parameterized type instances.

Static factory methods disadvantages:

  1. When providing only static factory methods, classes without public or protected constructors cannot be subclassed.
  2. They are not readily distinguishable from other static methods
束缚m 2024-07-21 08:53:05

来自 Gamma、Helm、Johnson 的 设计模式:可重用面向对象软件的元素的第 108 页, 时,请

使用工厂方法模式;

  • 当一个类无法预测它必须创建的对象类
  • 一个类希望它的子类指定它创建的对象;
  • 类将责任委托给几个辅助子类之一,并且您希望本地化哪个辅助子类是委托的知识

From page 108 of Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides.

Use the Factory Method pattern when

  • a class can't anticipate the class of objects it must create
  • a class wants its subclasses to specify the objects it creates
  • classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate
最冷一天 2024-07-21 08:53:05

默认情况下,应该首选构造函数,因为它们更易于理解和编写。 但是,如果您特别需要将对象的构造细节与其客户端代码所理解的语义含义解耦,那么最好使用工厂。

构造函数和工厂之间的区别类似于变量和指向变量的指针。 还有另一层间接性,这是一个缺点。 但还有另一个层面的灵活性,这是一个优势。 因此,在做出选择时,建议您进行成本与收益分析。

By default, constructors should be preferred, because they are simpler to understand and write. However, if you specifically need to decouple the construction niceties of an object from its semantic meaning as understood by the client code, you'd be better off using factories.

The difference between constructors and factories is analogous to, say, a variable and a pointer to a variable. There's another level of indirection, which is a disadvantage; but there's another level of flexibility too, which is an advantage. So while making a choice, you'd be well advised to do this cost versus benefit analysis.

愿与i 2024-07-21 08:53:05

仅当您需要对对象创建进行额外控制时才使用工厂,而使用构造函数无法做到这一点。

例如,工厂可以进行缓存。

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

Use a factory only when you need extra control with object creation, in a way that couldn't be done with constructors.

Factories have the possibility of caching for example.

Another way to use factories is in a scenario where you do not know the type you want to construct. Often you see this type of usage in plugin factory scenarios, where each plugin must derive from a baseclass or implement some kind of interface. The factory creates instances of classes that derive from the baseclass or that implement the interface.

热鲨 2024-07-21 08:53:05

除了另一个答案中提到的“efficient java”之外,另一本经典书籍(《干净的代码:敏捷软件工艺手册》)还建议:

优先选择静态工厂方法(带有描述参数的名称)而不是重载构造函数。

当构造函数重载时,请使用名称描述参数的静态工厂方法。 不要写

Complex complex = new Complex(23.0);

,而是写。

Complex complex = Complex.fromRealNumber(23.0);

本书甚至建议将Complex(float)构造函数设为私有,以强制用户调用静态工厂方法。

In addition to "effective java" mentioned in another answer, another classic book (Clean Code: A Handbook of Agile Software Craftsmanship) also suggests:

Prefer static factory methods (with names that describe the arguments) to overloaded constructors.

When constructors are overloaded, use static factory methods with names that describe the arguments. Don't write

Complex complex = new Complex(23.0);

but instead write

Complex complex = Complex.fromRealNumber(23.0);

The book goes as far as to suggest making the Complex(float) constructor private, to force the user to call the static factory method.

内心激荡 2024-07-21 08:53:05

引用自“Effective Java”,第二版,第 1 项:考虑静态工厂方法而不是构造函数,第 14 页。 5:

“请注意,静态工厂方法与工厂方法模式不同
摘自《设计模式》 [Gamma95,第 14 页] 107]。 静态工厂方法描述于
该项目在设计模式中没有直接等效项。”

A cite from "Effective Java", 2nd ed., Item 1: Consider static factory methods instead of constructors, p. 5:

"Note that a static factory method is not the same as the Factory Method pattern
from Design Patterns
[Gamma95, p. 107]. The static factory method described in
this item has no direct equivalent in Design Patterns."

无力看清 2024-07-21 08:53:05

CAD/CAM 应用程序的具体示例。

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

形状将通过工厂来制作。 因为虽然存在形状类,但每个形状都会根据形状的类型进行不同的设置。 在用户做出选择之前,我们不知道要初始化什么形状。

A concrete example from a CAD/CAM application.

A cutting path would be made by using a constructor. It is a series of lines and arcs defining a path to cut. While the series of lines and arcs can be different and have different coordinates it easily handled by passing a list into a constructor.

A shape would be would be made by using a factory. Because while there is a shape class each shape would be setup differently depending on what type of shape it is. We don't know what shape we are going to be initializing until the user makes a selection.

夏末 2024-07-21 08:53:05

假设,我有一个需要 id 值的类的构造函数。 构造函数使用此值从数据库填充类。

这个过程绝对应该在构造函数之外。

  1. 构造函数不应访问数据库。

  2. 构造函数的任务和原因是初始化数据成员并使用传递到构造函数的值建立类不变性

  3. 对于其他一切,更好的方法是使用静态工厂方法,或者在更复杂的情况下使用单独的工厂构建器类。

来自 Microsoft 的一些构造函数指南

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

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

Say, I have a constructor on a class which expects an id value. The constructor uses this value to populate the class from the database.

This process should definitely be outside a constructor.

  1. Constructor should not access database.

  2. The task and the reason for a constructor is to initialize data members and to establish class invariant using values passed into constructor.

  3. For everything else a better approach is to use static factory method or in more complex cases a separate factory or builder class.

Some constructor guide lines from Microsoft:

Do minimal work in the constructor. Constructors should not do much work other than to capture the constructor parameters. The cost of any other processing should be delayed until required.

And

Consider using a static factory method instead of a constructor if the semantics of the desired operation do not map directly to the construction of a new instance.

岁月打碎记忆 2024-07-21 08:53:05

有时,您在创建对象时必须检查/计算一些值/条件。 如果它可以抛出异常 - 构造函数是非常糟糕的方式。 所以你需要做这样的事情:

var value = new Instance(1, 2).init()
public function init() {
    try {
        doSome()
    }
    catch (e) {
        soAnotherSome()
    }
}

所有额外的计算都在 init() 中。 但只有作为开发人员的您才真正了解这个 init()。 当然,几个月后你就会忘记它。
但是,如果您有一个工厂 - 只需在一种方法中完成您需要的所有操作,并隐藏此 init() 以防止直接调用 - 所以没有问题。 使用这种方法不存在创建失败和内存泄漏的问题。

有人告诉过你关于缓存的事情。 很好。 但您还必须记住 Flyweight 模式,它与 Factory 方式一起使用非常好。

Sometimes you have to check/calculate some values/conditions while creating an object. And if it can throw an Exception - constructro is very bad way. So you need to do something like this:

var value = new Instance(1, 2).init()
public function init() {
    try {
        doSome()
    }
    catch (e) {
        soAnotherSome()
    }
}

Where all additional calculations are in init(). But only you as developer realy know about this init(). And of course, after months you just forget about it.
But if you have a factory - just do all you need in one method with hiding this init() from direct call - so no problems. With this approach is no problems with falling on creation and memory leaking.

Someone told you about caching. It's good. But you also have to remember about Flyweight pattern which is nice to use with Factory way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文