在构造函数中做了很多坏事吗?

maa*_*nus 64 java constructor

制作所有字段final通常都是一个好主意,但有时我发现自己在构造函数中做了所有事情.最近我最终得到了一个类,它实际上构建了构造函数中的所有内容,包括读取属性文件和访问数据库.

一方面,这就是该类的用途,它封装了读取的数据,我喜欢创建完全初始化的对象.构造函数并不复杂,因为它委托了大部分工作,所以它看起来很好.

另一方面,感觉有点奇怪.此外,在大约17:58的这次演讲中,有充分的理由不在构造函数中做很多工作.我想我可以通过传递适当的假人作为构造函数参数来消除这个问题.

问题仍然存在:构建器中的大量工作(甚至是所有工作)都不好吗?

Emi*_* L. 37

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

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

我一直在问自己这个问题.反对在我找到的构造函数中工作的动机是:

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

我的意见是......

...如果你的构造函数需要为它工作以遵守资源获取初始化(RAII)并且该类没有违反SRP并且DI被正确使用; 然后在构造函数中工作就是A-Okay!如果您想要防止使用完全初始化失败的类对象而不依赖于用户检查某些返回值,您甚至可以抛出异常.

  • 显然,当我回答时,我用C++设置了自己的思维,哦,derp.nvm仍然适用;) (2认同)

Dav*_*nco 18

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

在构造函数中工作并不像几年前那样"糟糕",因为异常处理并不像今天那样普遍和发展.您会注意到Google Tech的演讲主要从测试角度来看构造函数.构造函数在历史上一直非常难以调试,所以说话者是正确的,在构造函数中尽量少做更好.

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

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

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

    Config Conf = new Config();
    Log.info("Loading server.conf");
    Conf.doConfig();
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我决定不对构造函数做任何事情(它是空的)但是有一个doConfig()方法可以完成所有的磁盘i/o; 我经常认为该doConfig()方法毫无意义,我应该在构造函数中做所有事情.(毕竟我只查看配置文件一次.)

我认为这完全取决于你的代码,你不应该认为在构造函数中添加"东西"是件坏事.这就是构造函数的用途!有时,当真正所有类需要做的是加载配置文件时getThis,我们会被OOP(,, )带走.在这种情况下,只需将所有内容放在构造函数中并将其称为一天!setThatdoBark

  • 啊,所有那些永远不会发生的“假设”……不要过度设计您的代码。这就是重构的目的。 (3认同)
  • 您的配置示例存在缺陷,因为它违反了 SRP。如果您可以将配置存储在 RDBMS 而不是文件中怎么办?`Config config = new FileConfigurationProvider('server.conf').getConfig();` ...到此为止!没有构造函数的工作和更好的设计。 (2认同)

Mau*_*res 13

通常情况下,如果您的对象具有复杂的创建算法,您可以使用Builder或Factory简化它.特别是如果有要验证的前置条件来构建对象.

一旦使用建筑工地和工厂你开始建立你的对象,他们可以验证前置和后置条件,使你的代码的确定客户端将只能够访问一个完全初始化的对象,而不是一个半造一个,你甚至可以用时下在流行的流畅界面中创建您的对象并使其看起来很酷; D

new EmailMessage()
    .from("demo@guilhermechapiewski.com")
    .to("destination@address.com")
    .withSubject("Fluent Mail API")
    .withBody("Demo message")
    .send();
Run Code Online (Sandbox Code Playgroud)

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


pcj*_*zer 11

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

  • 很难为该类的其他方法编写单元测试,因为它想在构造函数中做很多事情,因此,我必须设置许多有效的东西或至少是模拟(DB,文件,等等)对于最简单的单元测试.
  • 很难为构造函数本身编写单元测试.无论如何,将大量具有多样化责任的代码放入一个块中甚至是一个坏主意.(单一责任原则.)
  • 由于前一个原因,很难使用该课程.例如,它完全阻止我实现一些延迟的init方法,因为它需要在调用构造函数时的所有内容.好的,我可以将lazy init方法编写到构造函数中,很好.
  • 迟早我意识到重用一些放在构造函数中的代码部分是有意义的.好吧,当我第一次编写构造函数时,我也认为那些代码部分将永远用于那里.
  • 当我想扩展该类并在超级构造函数的逻辑之前或之中插入一些逻辑时,它根本不起作用,因为在扩展类的构造函数中要做的第一件事就是调用超级构造函数.

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

通常我只是将一些字段初始化放入构造函数中,并在每个人都加入时调用init方法.

  • 可以,但是做很多不是问题。违反SRP是问题所在。 (2认同)