在构造函数中做很多事情是不好的吗?

发布于 2024-11-29 03:55:28 字数 1431 浏览 0 评论 0原文

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

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

发布评论

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

评论(5

北座城市 2024-12-06 03:55:28

我认为“在构造函数中工作”是可以的......

只要你不违反 单一职责原则 (SRP) 并坚持使用 依赖注入 (DI)

我最近也一直在问自己这个问题。我发现反对在构造函数中工作的动机是:

  • 它使得测试变得困难
    • 我见过的所有示例都是 DI没有被使用。这实际上并不是构造函数执行实际工作的错误。
  • 您可能不需要构造函数计算的所有结果,这会浪费处理时间,并且很难单独测试。
    • 这基本上违反了SRP,这不是构造函数按照所说的方式工作的错误。
  • 旧的编译器在构造函数中抛出异常时遇到了麻烦,因此除了在构造函数中分配字段之外,您不应该执行任何其他操作。
    • 我认为编写新代码时考虑历史编译器缺陷并不是一个好主意。如果我们这样做的话,我们还不如废除 C++11 和所有好的东西。

我的观点是......

如果你的构造函数需要做一些工作来遵守 资源获取即初始化 (RAII) 并且该类不违反 SRPDI 正确使用;然后在构造函数中进行工作就OK了!如果您想阻止使用初始化完全失败的类对象,而不是依赖用户检查某些返回值,您甚至可以抛出异常。

I think that "Doing work in the constructor" is okay...

... as long as you don't violate Single Responsibility Principle (SRP) and stick to using Dependency Injection (DI).

I have been asking myself this question too lately. And the motivation against doing work in the constructor that I have found are either:

  • It makes it hard to test
    • All examples I have seen have been where DI wasn't used. It wasn't actually the fault of the constructor doing actual work.
  • You might not need all the results that your constructor calculates, wasting processing time and it's hard to test in isolation.
    • This is basically a violation of SRP, not a fault of the constructor doing work per say.
  • Old compilers have/had trouble with exceptions thrown in constructors, hence you shouldn't do anything other than assign fields in constructors.
    • I don't think it's a good idea to write new code taking historical compiler deficiencies into account. We might as well do away with C++11 and all that is good all together if we do.

My opinion is that...

... if your constructor needs to do work for it to adhere to Resource Acquisition Is Initialization (RAII) and the class does not violate SRP and DI is properly used; Then doing work in the constructor is A-Okay! You can even throw an exception if you'd like to prevent usage of a class object whose initialization failed completely instead of relying on the user to check some return value.

私藏温柔 2024-12-06 03:55:28

这是一个非常开放式的问题,所以我的答案将尽可能笼统......

在构造函数中工作并不像几年前那样“糟糕”,当时异常处理还没有那么普遍,并且演变成了今天的样子。您会注意到 Google Tech 演讲主要从测试角度着眼于构造函数。构造函数在历史上一直非常难以调试,因此发言者是正确的,在构造函数中执行尽可能少的操作会更好。

话虽如此,您会注意到他还涉及依赖注入/提供者模式,该模式因使构造函数复杂化而臭名昭著。在这种情况下,最好在构造函数中仅保留提供者/DI 代码。同样,答案取决于您使用的模式以及您的代码如何“适合”在一起。

使用构造函数的全部目的是创建一个可以立即使用的简洁对象;即new Student("David Titarenco", "Senior", 3.5)。没有必要执行 david.initialize() ,因为这完全是愚蠢的。

例如,以下是我的一些生产代码:

    Config Conf = new Config();
    Log.info("Loading server.conf");
    Conf.doConfig();

在上面的情况下,我决定不对构造函数执行任何操作(它是空的),而是使用一个执行所有磁盘 I/O 的 doConfig() 方法;我经常认为 doConfig() 方法毫无意义,我应该在构造函数中完成所有操作。 (毕竟,我只检查配置文件一次。)

我认为这完全取决于您的代码,您不应该认为将“东西”放入构造函数中是一件坏事。这就是构造函数的用途!有时,当类真正需要做的就是加载配置文件时,我们会沉迷于 OOP(getThissetThatdoBark)。在这种情况下,只需将所有内容放入构造函数中即可,今天就到此为止!

This is a very open-ended question, so my answer will try to be as general as possible...

Doing work in constructors isn't as "bad" as it used to be years ago when exception handling wasn't as prevalent and evolved as it is today. You'll notice that the Google Tech talk primarily looks at constructors from a Testing perspective. Constructors have been historically very very difficult to debug so the speaker is correct that doing as little as possible in a constructor is better.

With that said, you'll notice that he's also touching on dependency injection/the provider pattern which is notorious for complicating constructors. In such a case, leaving ONLY provider/DI code in the constructor is preferred. Again, the answer depends on what patterns you're using and how your code "fits" together.

The entire point of using a constructor is to create a neat object that can be used immediately; i.e. new Student("David Titarenco", "Senior", 3.5). There's no need to do david.initialize() as it would be entirely silly.

Here's some of my production code, for example:

    Config Conf = new Config();
    Log.info("Loading server.conf");
    Conf.doConfig();

In the above case, I decided not to do anything with the constructor (it's empty) but have a doConfig() method that does all the disk i/o; I've often thought that the doConfig() method is just pointless and I should do everything in the constructor. (I only check out the config file once, after all.)

I think that it's entirely dependent on your code and you shouldn't think that putting "stuff" in your constructor is a bad thing. That's what constructors are for! Sometimes we get carried away with OOP (getThis, setThat, doBark) when really all a class needs to do is a load a config file. In such cases, just put everything in the constructor and call it a day!

撩人痒 2024-12-06 03:55:28

当我在构造函数中放入太多代码时,我遇到了以下问题:

  • 很难为该类的其他方法编写单元测试,
    因为它想在构造函数中做很多事情,因此,我
    必须设置很多有效的东西或者至少是模拟(数据库,文件,
    无论如何)用于最简单的单元测试。
  • 为构造函数本身编写单元测试很困难。反正,
    将大量具有不同职责的代码放入一个代码中
    阻止甚至是一个坏主意。 (单一职责原则。)
  • 由于前面的原因,很难使用该类。例如,
    它完全阻止我实现一些延迟的初始化方法,
    因为它在调用的那一刻需要一切
    构造函数。好的,我可以将惰性初始化方法写入
    构造函数,不错。
  • 迟早我意识到重用某些代码部分是有意义的
    它们被放置在构造函数中。嗯,当我第一次写的时候
    构造函数我还认为这些代码部分将仅使用
    在那里,永远。
  • 当我想扩展该类并在或之前插入一些逻辑时
    进入超级构造函数的逻辑,它根本不起作用,因为
    在扩展类的构造函数中要做的第一件事是调用
    超级的一个。

所以,是的,在我看来,在构造函数中做很多事情是一个坏主意。

一般来说,我只是将一些字段初始化放入构造函数中,并创建一个 init 方法以在每个人都在船上时调用。

I faced the following problems when I put too much code into the constructor:

  • It was hard to write unit tests for other methods of that class,
    because it wanted to do a lots of things in the constructor, thus, I
    had to set up a lots of valid things or at least mocks (DB, file,
    whatever) for the simplest unit tests.
  • It was hard to write unit tests for the constructor itself. Anyway,
    putting a lots of code with diversified responsibilites into one
    block is even a bad idea. (Single Responsibility Principle.)
  • For the former reasons, it was hard to use that class. For example,
    it completely prevented me to implement some deferred init methods,
    because it needed everything in the moment of invoking the
    constructor. Ok, I could write the lazy init method into the
    constructor, nice.
  • Sooner or later I realized that it had sense to reuse some code parts
    which are placed in the constructor. Well, when I first wrote the
    constructor I also thought that those code parts will be used just
    there, forever.
  • When I wanted to extend that class and insert some logic before or
    into the super constructor's logic, it simply didn't work because the
    first thing to do in the extended class' constructor is to invoke the
    super's one.

So yes, doing a lot in constructor is a bad idea in my opinion.

Generally I just put some field initializations into the constructor and make an init method to invoke when everybody is on board.

无言温柔 2024-12-06 03:55:28

通常,如果您的对象具有复杂的创建算法,您可能可以使用构建器或工厂来简化它。特别是如果需要验证构建对象的先决条件。

一旦您开始使用构建器和工厂来构建对象,它们就可以验证前置条件和后置条件,并确保代码的客户端只能访问完全初始化的对象,而不是半成品对象,您甚至可以使用当今流行的流畅界面来创建您的对象并使其看起来很酷;D

new EmailMessage()
    .from("[email protected]")
    .to("[email protected]")
    .withSubject("Fluent Mail API")
    .withBody("Demo message")
    .send();

显然这不是您的情况,因为这不是使用构建器,但它很像您可以构建的东西来使您的构造函数做更少的工作,让你的代码看起来更简单。

It usually is, if your object has a complicated creation algorithm you could probably simplify it using a Builder or a Factory. Specially if there are pre-conditions to be validated to build the object.

Once your start using Builders and Factories to build your objects they can validate the pre and post conditions and make sure clients of your code will only be able to access a fully initialized object and not a half-made one, you can even use the nowadays in vogue fluent interfaces to create your object and make it look cool ;D

new EmailMessage()
    .from("[email protected]")
    .to("[email protected]")
    .withSubject("Fluent Mail API")
    .withBody("Demo message")
    .send();

Obviously this isn't really your case, as this is not using a Builder, but it's much like something you could build to make your constructor do less work and make your code look simpler.

颜漓半夏 2024-12-06 03:55:28

在我看来,拥有构造函数和析构函数很好,但不要在其中做太多工作。特别是文件/数据库访问,除非它非常特定于该类。您希望保持构造函数/析构函数的轻量性,以使程序感觉流畅。有时,就像您已经遇到的情况一样,构造函数基本上完成了所有工作。有一种方法可以让事情变得更轻松。这个概念/范式称为惰性求值。这个想法是接受输入而不执行任何操作(例如在构造函数中),但在需要请求计算时使用输入。

示例:假设您有一个类,它读取文件、解析它并告诉您信息,例如文件中所有数字的总和。您可以在构造函数中完成这一切。使用惰性求值,您只需打开文件,并有一个 getTotalSum() 函数。当调用时,它将进行解析并给出结果。这样您还可以使用 getBestFit() 来获得最佳拟合线。有时,您不想获得最佳匹配,但对于某些输入,您确实想要获得最佳匹配。这样,用户在决定要做什么之前就不会等待构造函数进行计算。

另一个例子:假设您有一个加载 20 张图像的视图。但只显示了 5 个,并且构造函数采用图像数组来显示。您可以在构造函数中加载它们,但从用户的角度来看,一开始会感觉很慢。或者您可以加载 1 个“正在加载”图片并一次加载 1 个图像。当用户滚动时,根据显示/需要加载更多图像。

当然,第一个问题是您会在以后而不是构造函数中发现诸如无效图片之类的错误。您始终可以自己执行简单的检查,以在某种程度上预先验证输入(例如检查密码是否正确)。

Having constructors and destructors in my opinion is good, but not to do too much work in them. Especially not file/database access unless its very very specific to the class. You want to keep your constructors/destructors light to keep your program feeling fluid. Sometimes like already you have come to a case where the constructor does essentially all the work. There is a way to make things lighter. The concept/paradigm is called lazy evaluation. The idea is to accept inputs and do nothing (e.g. in constructor) but make use of the inputs when you need a calculation requested.

Example: Lets say you have a class that reads a file, parses it and tells you info like the sum of all numbers in the file. You can do this all in the constructor. Using lazy evaluation you will merely open the file, and have a getTotalSum() function. When called it will do the parsing and give you the result. This way you can also have getBestFit() to get best fit line. Sometimes you dont want to get best fit and for some inputs you do. This way the user will not be waiting for the constructor to do the calculations before the user decides what to do.

another example: lets say you have a view that loads 20 images. But only 5 are shown, and the constructor takes an array of the images to show. You can load them all in the constructor, but from a users perspective this will feel slow at the beginning. Or you can load 1 "loading" picture and load 1 image at a time. And as the user scrolls load more of the images on a as shown/needed basis.

Of course 1 problem is that you find out of errors like invalid pictures later down the road instead of the constructor. You can always perform simple checks for yourself to pre-validate the input to some degree (e.g. check for correct password).

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