使用很长的初始化方法是不好的做法吗?

发布于 2024-08-28 08:47:48 字数 507 浏览 4 评论 0原文

很多 人们对函数大小争论不休。他们说函数一般应该很短。人们的意见各不相同,从大约 15 行到“大约一个屏幕”,今天可能约为 40-80 行。
此外,函数应该始终只完成一项任务。

然而,有一种函数经常在我的代码中的两个标准中失败:初始化函数。

例如,在音频应用程序中,必须设置音频硬件/API,必须将音频数据转换为合适的格式,并且必须正确初始化对象状态。这显然是三个不同的任务,根据 API,这可以轻松跨越 50 多行。

init-functions 的特点是它们通常只被调用一次,所以不需要重复使用任何组件。您还会将它们分解为几个较小的函数吗?您认为大的初始化函数可以吗?

Many people have argued about function size. They say that functions in general should be pretty short. Opinions vary from something like 15 lines to "about one screen", which today is probably about 40-80 lines.
Also, functions should always fulfill one task only.

However, there is one kind of function that frequently fails in both criteria in my code: Initialization functions.

For example in an audio application, the audio hardware/API has to be set up, audio data has to be converted to a suitable format and the object state has to properly initialized. These are clearly three different tasks and depending on the API this can easily span more than 50 lines.

The thing with init-functions is that they are generally only called once, so there is no need to re-use any of the components. Would you still break them up into several smaller functions would you consider big initialization functions to be ok?

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

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

发布评论

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

评论(9

菩提树下叶撕阳。 2024-09-04 08:47:48

我仍然会按任务分解函数,然后从面向公众的初始化函数中调用每个较低级别的函数:

void _init_hardware() { }
void _convert_format() { }
void _setup_state() { }

void initialize_audio() {
    _init_hardware();
    _convert_format();
    _setup_state();
}

编写简洁的函数既要隔离错误和更改,又要保持可读性。如果您知道故障发生在 _convert_format() 中,则可以更快地找到导致错误的大约 40 行。如果您提交的更改仅涉及一个功能,则同样的情况也适用。

最后一点,我非常频繁地使用assert(),这样我就可以“经常失败并提前失败”,并且函数的开头是几个健全性检查断言的最佳位置。保持函数简短可以让您根据更狭窄的职责集更彻底地测试函数。对一个执行 10 件不同事情的 400 行函数进行单元测试非常困难。

I would still break the function up by task, and then call each of the lower level functions from within my public-facing initialize function:

void _init_hardware() { }
void _convert_format() { }
void _setup_state() { }

void initialize_audio() {
    _init_hardware();
    _convert_format();
    _setup_state();
}

Writing succinct functions is as much about isolating fault and change as keeping things readable. If you know the failure is in _convert_format(), you can track down the ~40 lines responsible for a bug quite a bit faster. The same thing applies if you commit changes that only touch one function.

A final point, I make use of assert() quite frequently so I can "fail often and fail early", and the beginning of a function is the best place for a couple of sanity-checking asserts. Keeping the function short allows you to test the function more thoroughly based on its more narrow set of duties. It's very hard to unit-test a 400 line function that does 10 different things.

心房的律动 2024-09-04 08:47:48

如果分解成更小的部分可以使代码更好地结构化和/或更易读 - 无论函数做什么,都要这样做。这与行数无关,而与代码质量有关。

If breaking into smaller parts makes code better structured and/or more readable - do it no matter what the function does. It not about the number of lines it's about code quality.

情域 2024-09-04 08:47:48

我仍然会尝试将功能分解为逻辑单元。它们应该尽可能长或短。例如:

SetupAudioHardware();
ConvertAudioData();
SetupState();

为它们分配清晰的名称使一切更加直观和可读。此外,将它们分开可以使将来的更改和/或其他程序更容易重用它们。

I would still try to break up the functions into logical units. They should be as long or as short as makes sense. For example:

SetupAudioHardware();
ConvertAudioData();
SetupState();

Assigning them clear names makes everything more intuitive and readable. Also, breaking them apart makes it easier for future changes and/or other programs to reuse them.

新人笑 2024-09-04 08:47:48

在这种情况下,我认为这取决于个人喜好。我更喜欢让函数只做一件事,所以我会将初始化分成单独的函数,即使它们只被调用一次。然而,如果有人想在一个函数中完成这一切,我不会太担心(只要代码清晰)。还有更重要的事情需要争论(例如大括号是否属于自己的单独行)。

In a situation like this I think it comes down to a matter of personal preference. I prefer to have functions do only one thing so I would split the initialization into separate functions, even if they are only called once. However, if someone wanted to do it all in a single function I wouldn't worry about it too much (as long as the code was clear). There are more important things to argue about (like whether curly braces belong on their own separate line).

八巷 2024-09-04 08:47:48

首先,应该使用工厂而不是初始化函数。也就是说,您没有 initialize_audio(),而是有一个 new AudioObjectFactory(您可以在这里想一个更好的名称)。这保持了关注点分离。

然而,也要小心不要太早抽象。显然您已经有两个问题:1)音频初始化和2)使用该音频。例如,直到您抽象出要初始化的音频设备,或者在初始化期间配置给定设备的方式,您的工厂方法(audioObjectFactory.Create() 或其他)都应该保留只是一种大方法。早期的抽象只会混淆设计。

请注意,audioObjectFactory.Create() 不是可以进行单元测试的东西。测试它是一个集成测试,在它的某些部分可以被抽象之前,它仍然是一个集成测试。稍后,您可能会发现您有多个不同的工厂用于不同的配置;此时,将硬件调用抽象到接口中可能会很有帮助,这样您就可以创建单元测试以确保各个工厂以正确的方式配置硬件。

First, a factory should be used instead of an initialization function. That is, rather than have initialize_audio(), you have a new AudioObjectFactory (you can think of a better name here). This maintains separation of concerns.

However, be careful also not to abstract too early. Clearly you do have two concerns already: 1) audio initialization and 2) using that audio. Until, for example, you abstract the audio device to be initialized, or the way a given device may be configured during initialization, your factory method (audioObjectFactory.Create() or whatever), should really be kept to just one big method. Early abstraction serves only to obfuscate design.

Note that audioObjectFactory.Create() is not something that can be unit-tested. Testing it is an integration test, and until there are parts of it that can be abstracted, it will remain an integration test. Later on, you may find that the you have multiple different factories for different configurations; at that point, it might be beneficial to abstract the hardware calls into an interface, so you that you can create unit tests to ensure the various factories configure the hardware in a proper way.

呆° 2024-09-04 08:47:48

如果您有很多组件需要相互插入,那么拥有一个大的方法当然是相当自然的——即使每个组件的创建在可行的情况下被重构为一个单独的方法。

一种替代方法是使用依赖注入框架(例如 Spring、Castle Windsor、Guice 等)。这有明确的优点和缺点......虽然通过一种大方法工作可能会非常痛苦,但你至少很好地了解所有内容的初始化位置,并且无需担心可能会发生什么“魔法” 。但话又说回来,初始化在部署后就无法更改(例如,对于 Spring 的 XML 文件就可以更改)。

我认为设计代码的主体以便它可以被注入是有意义的 - 但无论注入是通过框架还是只是硬编码(并且可能很长)的初始化调用列表这个选择可能会根据不同的项目而改变。在这两种情况下,除了运行应用程序之外,很难测试结果。

If you have a lot of components the need to be plugged into each other, it can certainly be reasonably natural to have a large method - even if the creation of each component is refactored into a separate method where feasible.

One alternative to this is to use a Dependency Injection framework (e.g. Spring, Castle Windsor, Guice etc). That has definite pros and cons... while working your way through one big method can be quite painful, you at least have a good idea of where everything is initialized, and there's no need to worry about what "magic" might be going on. Then again, the initialization can't be changed after deployment (as it can with an XML file for Spring, for example).

I think it makes sense to design the main body of your code so that it can be injected - but whether that injection is via a framework or just a hard-coded (and potentially long) list of initialization calls is a choice which may well change for different projects. In both cases the results are hard to test other than by just running the application.

街道布景 2024-09-04 08:47:48

我认为尝试计算行数并据此确定函数是错误的方法。对于初始化代码之类的东西,我通常有一个单独的函数,但主要是为了使 Load 或 Init 或 New 函数不会混乱和混乱。如果您可以像其他人建议的那样将其分成几个任务,那么您可以将其命名为有用的名称并帮助组织。即使您只调用它一次,这也不是一个坏习惯,并且您经常会发现有时您可能想要重新初始化事物并可以再次使用该函数。

I think it's the wrong approach to try and count the number of lines and determine functions based on that. For something like initialization code I often have a separate function for it, but mostly so that the Load or Init or New functions aren't cluttered and confusing. If you can separate it into a few tasks like others have suggested, then you can name it something useful and help organize. Even if you are calling it just once, it's not a bad habit, and often you find that there are other times when you may want to re-init things and can use that function again.

星星的轨迹 2024-09-04 08:47:48

只是想我会把它扔在那里,因为它还没有被提及 - 外观模式 有时被引用为复杂子系统的接口。我自己并没有做过太多的事情,但这些隐喻通常类似于打开计算机(需要几个步骤),或打开家庭影院系统(打开电视、打开接收器、关掉灯等)。 .)

根据代码结构,可能值得考虑抽象出大型初始化函数。我仍然同意 meagar 的观点,尽管将函数分解为 _init_X()、_init_Y() 等是一个很好的方法。即使您不打算在下一个项目中重用此代码中的注释,当您对自己说“我如何初始化该 X 组件?”时,返回并挑选它会容易得多与从较大的函数中挑选它相比,使用较小的 _init_X() 函数进行比较,尤其是当 X 初始化分散在整个函数中时。

Just thought I'd throw this out there, since it hasn't been mentioned yet - the Facade Pattern is sometimes cited as an interface to a complex subsystem. I haven't done much with it myself, but the metaphors are usually something like turning on a computer (requires several steps), or turning on a home theater system (turn on TV, turn on receiver, turn down lights, etc...)

Depending on the code structure, might be something worth considering to abstract away your large initialization functions. I still agree with meagar's point though that breaking down functions into _init_X(), _init_Y(), etc. is a good way to go. Even if you aren't going to reuse comments in this code, on your next project, when you say to yourself, "How did I initialize that X-component?", it'll be much easier to go back and pick it out of the smaller _init_X() function than it would be to pick it out of a larger function, especially if the X-initialization is scattered throughout it.

伪心 2024-09-04 08:47:48

正如您所标记的,函数长度是一个非常主观的问题。然而,标准的最佳实践是隔离经常重复和/或可以作为其自己的实体运行的代码。例如,如果您的初始化函数正在加载将由特定库使用的库文件或对象,则该代码块应该模块化。

话虽如此,有一个很长的初始化方法也不错,只要它不长,因为有很多重复的代码或其他可以抽象出来的片段。

希望有帮助,
卡洛斯·努涅斯

Function length is, as you tagged, a very subjective matter. However, a standard best-practice is to isolate code that is often repeated and/or can function as its own entity. For instance, if your initialization function is loading library files or objects that will be used by a specific library, that block of code should be modularized.

With that said, it's not bad to have an initialization method that's long, as long as it's not long because of lots of repeated code or other snippets that can be abstracted away.

Hope that helps,
Carlos Nunez

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