克服“以后修复”的坏习惯

时间:2021-04-24 12:51:25

When I start writing code from scratch, I have a bad habit of quickly writing everything in one function, the whole time thinking "I'll make it more modular later". Then when later comes along, I have a working product and any attempts to fix it would mean creating functions and having to figure out what I need to pass.

当我从头开始编写代码时,我养成了在一个函数中快速编写所有内容的坏习惯,整个时间都在考虑“我将在以后更加模块化”。然后,当后来出现时,我有一个工作产品,任何修复它的尝试都意味着创建功能,并且必须弄清楚我需要通过什么。

It gets worst because it becomes extremely difficult to redesign classes when your project is almost done. For example, I usually do some planning before I start writing code, then when my project is done, I realized I could have made the classes more modular and/or I could have used inheritance. Basically, I don't think I do enough planning and I don't get more than one-level of abstraction.

它变得最糟糕,因为当你的项目几乎完成时重新设计类变得非常困难。例如,我通常在开始编写代码之前做一些规划,然后当我的项目完成时,我意识到我可以使类更加模块化和/或我可以使用继承。基本上,我认为我没有做足够的计划,而且我不会获得超过一个级别的抽象。

So in the end, I'm stuck with a program with a large main function, one class and a few helper functions. Needless to say, it is not very reusable.

所以最后,我坚持使用一个具有大型主函数,一个类和一些辅助函数的程序。不用说,它不是非常可重复使用的。

Has anybody had the same problem and have any tips to overcome this? One thing I had in mind was to write the main function with pseduocode (without much detail but enough to see what objects and functions they need). Essentially a top-down approach.

有没有人有同样的问题,有任何提示可以克服这个问题?我想到的一件事是用pseduocode编写main函数(没有太多细节,但足以看到他们需要什么对象和函数)。本质上是一种自上而下的方法。

Is this a good idea? Any other suggestions?

这是一个好主意吗?还有其他建议吗?

9 个解决方案

#1


I take the completely opposite approach when writing something on-the-fly without a big design phase beforehand. That is, I write the main function the way it would look "in a perfect world" using imaginary objects and methods, then create skeletons of everything so the program compiles, then go back and make it work. This ensures a modular design, and the high-level code is very easy to understand. If anything, the drawback is the code can become too modular, as it's tempting to write doFoo() instead of implementing foo inline.

在事先没有大型设计阶段的情况下即时编写内容时,我采取完全相反的方法。也就是说,我使用虚构的对象和方法编写主要函数,看起来像“在一个完美的世界中”,然后创建所有内容的骨架,以便程序编译,然后返回并使其工作。这确保了模块化设计,并且高级代码非常易于理解。如果有的话,缺点是代码可能变得过于模块化,因为编写doFoo()而不是实现foo内联是很诱人的。

#2


"First we make our habits, then they make us."

“首先,我们养成了习惯,然后才能养成我们。”

This seems to apply for both good and bad habits. Sounds like a bad one has taken hold of you.

这似乎适用于好习惯和坏习惯。听起来像是一个糟糕的人已经掌握了你。

Practice being more modular up front until it's "just the way I do things."

练习更加模块化,直到它“只是我做事的方式”。

#3


Yes, the solution is easy, although it takes time to get used to it. Never claim there will be a "later", where you sit down and just do refactoring. Instead, continue adding functionality to your code (or tests) and during this phase perform small, incremental refactorings. The "later" will basically be "always", but hidden in the phase where you are actually doing something new every time.

是的,解决方案很简单,虽然需要时间来适应它。从来没有声称会有一个“后来”,你坐下来,只是做重构。相反,继续向代码(或测试)添加功能,并在此阶段执行小的增量重构。 “后来”基本上是“永远”,但隐藏在你每次都在做新事物的阶段。

#4


My rule of thumb is that anything longer than 20 LoC should be clean. IME every project stands on a few "just-a-proof-of-concept"s that were never intended to end up in production code. Since this seems inevitable though, even 20 lines of proof-of-concept code should be clear, because they might end up being one of the foundations of a big project.

我的经验法则是,任何超过20 LoC的东西都应该是干净的。 IME的每个项目都代表着一些从未打算在生产代码中出现的“仅仅是一个概念验证”。由于这似乎是不可避免的,即使是20行概念验证代码也应该清楚,因为它们最终可能成为一个大项目的基础之一。

My approach is top-down. I write

我的方法是自上而下的。我写

while( obj = get_next_obj(data) ) {
  wibble(obj);
  fumble(obj);
  process( filter(obj) );
}

and only start to write all these functions later. (Usually they are inline and go into the unnamed namespace. Sometimes they turn out to be one-liners and then I might eliminate them later.)

并且稍后才开始编写所有这些功能。 (通常它们是内联的并进入未命名的命名空间。有时它们会变成单行,然后我可能会在以后删除它们。)

This way I also avoid to have to comment the algorithms: The function names are explanation enough.

这样我也避免了对算法的评论:函数名称足够解释。

#5


I find the TDD Red-Green-Refactor discipline works wonders.

我发现TDD Red-Green-Refactor学科可以创造奇迹。

#6


You pretty much identified the issue. Not having enough planning. Spend some time analyzing the solution you're going to develop, break it down into pieces of functionality, identify how it would be best to implement them and try to separate the layers of the application (UI, business logic, data access layer, etc).

你几乎已经确定了这个问题。没有足够的计划。花一些时间分析您将要开发的解决方案,将其细分为多个功能,确定如何最好地实现它们并尝试分离应用程序的各个层(UI,业务逻辑,数据访问层等) )。

Think in terms of OOP and refactor as early as it makes sense. It's a lot cheaper than doing it after everything is built.

尽可能早地考虑OOP和重构。一切都建好后,它比做它便宜很多。

#7


Write the main function minimally, with almost nothing in it. In most gui programs, sdl games programs, open gl, or anything with any kind of user interface at all, the main function should be nothing more than an event eating loop. It has to be, or there will always be long stretches of time where the computer seems unresponsive, and the operating system thinks considers maybe shutting it down because it's not responding to messages.

最低限度地编写主函数,几乎没有任何内容。在大多数gui程序,sdl游戏程序,open gl或任何具有任何类型用户界面的任何东西中,主要功能应该只是一个事件循环。它必须是,或者总是有很长一段时间,计算机似乎没有响应,并且操作系统认为可能会关闭它,因为它没有响应消息。

Once you get your main loop, quickly lock that down, only to be modified for bug fixes, not new functionality. This may just end up displacing the problem to another function, but having a monilithic function is rather difficult to do in an event based application anyway. You'll always need a million little event handlers.

一旦你得到你的主循环,快速锁定它,只修改错误修复,而不是新功能。这可能最终将问题转移到另一个函数,但无论如何,在基于事件的应用程序中具有monilithic函数是相当困难的。你总是需要一百万个小事件处理程序。

Maybe you have a monolithic class. I've done that. Mainly the way to deal with it is to try and keep a mental or physical map of dependencies, and note where there's ... let's say, perforations, fissures where a group of functions doesn't explicitly depend on any shared state or variables with other functions in the class. There you can spin that cluster of functions off into a new class. If it's really a huge class, and really tangled up, I'd call that a code smell. Think about redesigning such a thing to be less huge and interdependant.

也许你有一个单一的课程。我做到了。主要处理它的方法是尝试保持依赖关系的心理或物理映射,并注意哪里有... ...说,穿孔,裂缝,其中一组函数没有明确依赖于任何共享状态或变量与班上的其他功能。在那里,您可以将该功能集关闭到一个新类中。如果它真的是一个庞大的阶级,并且真的很纠结,我会称之为代码味道。考虑重新设计这样的东西,以减少巨大和相互依赖。

Another thing you can do is as you're coding, note that when a function grows to a size where it no longer fits on a single screen, it's probably too big, and at that point start thinking about how to break it down into multiple smaller functions.

您可以做的另一件事是在编码时,请注意当函数增长到不再适合单个屏幕的大小时,它可能太大了,并且在那时开始考虑如何将其分解为多个功能较小。

#8


Refactoring is a lot less scary if you have good tools to do it. I see you tagged your question as "C++" but the same goes for any language. Get an IDE where extracting and renaming methods, extracting variables, etc. is easy to do, and then learn how to use that IDE effectively. Then the "small, incremental refactorings" that Stefano Borini mentions will be less daunting.

如果你有很好的工具,那么重构就不那么可怕了。我看到你将你的问题标记为“C ++”,但任何语言都是如此。获取一个IDE,其中提取和重命名方法,提取变量等很容易,然后学习如何有效地使用该IDE。然后Stefano Borini提到的“小的,渐进的重构”将不那么令人生畏。

#9


Your approach isn't necessarily bad -- earlier more modular design might end up as over-engineering.

您的方法并不一定是坏的 - 早期的模块化设计可能最终会过度工程化。

You do need to refactor -- this is a fact of life. The question is when? Too late, and the refactoring is too big a task and too risk-prone. Too early, and it might be over-engineering. And, as time goes on, you will need to refactor again .. and again. This is just part of the natural life-cycle of software.

你确实需要重构 - 这是生活中的事实。问题是什么时候?太晚了,重构太重要了,太冒险了。太早了,它可能是过度工程化的。而且,随着时间的推移,你将需要再次重构。这只是软件自然生命周期的一部分。

The trick is to refactor soon, but not too soon. And frequently, but not too frequently. How soon and how frequently? That's why it's a art and not a science :)

诀窍很快就会重构,但不会太快。经常,但不是太频繁。多久和多久?这就是为什么它是一门艺术,而不是一门科学:)

#1


I take the completely opposite approach when writing something on-the-fly without a big design phase beforehand. That is, I write the main function the way it would look "in a perfect world" using imaginary objects and methods, then create skeletons of everything so the program compiles, then go back and make it work. This ensures a modular design, and the high-level code is very easy to understand. If anything, the drawback is the code can become too modular, as it's tempting to write doFoo() instead of implementing foo inline.

在事先没有大型设计阶段的情况下即时编写内容时,我采取完全相反的方法。也就是说,我使用虚构的对象和方法编写主要函数,看起来像“在一个完美的世界中”,然后创建所有内容的骨架,以便程序编译,然后返回并使其工作。这确保了模块化设计,并且高级代码非常易于理解。如果有的话,缺点是代码可能变得过于模块化,因为编写doFoo()而不是实现foo内联是很诱人的。

#2


"First we make our habits, then they make us."

“首先,我们养成了习惯,然后才能养成我们。”

This seems to apply for both good and bad habits. Sounds like a bad one has taken hold of you.

这似乎适用于好习惯和坏习惯。听起来像是一个糟糕的人已经掌握了你。

Practice being more modular up front until it's "just the way I do things."

练习更加模块化,直到它“只是我做事的方式”。

#3


Yes, the solution is easy, although it takes time to get used to it. Never claim there will be a "later", where you sit down and just do refactoring. Instead, continue adding functionality to your code (or tests) and during this phase perform small, incremental refactorings. The "later" will basically be "always", but hidden in the phase where you are actually doing something new every time.

是的,解决方案很简单,虽然需要时间来适应它。从来没有声称会有一个“后来”,你坐下来,只是做重构。相反,继续向代码(或测试)添加功能,并在此阶段执行小的增量重构。 “后来”基本上是“永远”,但隐藏在你每次都在做新事物的阶段。

#4


My rule of thumb is that anything longer than 20 LoC should be clean. IME every project stands on a few "just-a-proof-of-concept"s that were never intended to end up in production code. Since this seems inevitable though, even 20 lines of proof-of-concept code should be clear, because they might end up being one of the foundations of a big project.

我的经验法则是,任何超过20 LoC的东西都应该是干净的。 IME的每个项目都代表着一些从未打算在生产代码中出现的“仅仅是一个概念验证”。由于这似乎是不可避免的,即使是20行概念验证代码也应该清楚,因为它们最终可能成为一个大项目的基础之一。

My approach is top-down. I write

我的方法是自上而下的。我写

while( obj = get_next_obj(data) ) {
  wibble(obj);
  fumble(obj);
  process( filter(obj) );
}

and only start to write all these functions later. (Usually they are inline and go into the unnamed namespace. Sometimes they turn out to be one-liners and then I might eliminate them later.)

并且稍后才开始编写所有这些功能。 (通常它们是内联的并进入未命名的命名空间。有时它们会变成单行,然后我可能会在以后删除它们。)

This way I also avoid to have to comment the algorithms: The function names are explanation enough.

这样我也避免了对算法的评论:函数名称足够解释。

#5


I find the TDD Red-Green-Refactor discipline works wonders.

我发现TDD Red-Green-Refactor学科可以创造奇迹。

#6


You pretty much identified the issue. Not having enough planning. Spend some time analyzing the solution you're going to develop, break it down into pieces of functionality, identify how it would be best to implement them and try to separate the layers of the application (UI, business logic, data access layer, etc).

你几乎已经确定了这个问题。没有足够的计划。花一些时间分析您将要开发的解决方案,将其细分为多个功能,确定如何最好地实现它们并尝试分离应用程序的各个层(UI,业务逻辑,数据访问层等) )。

Think in terms of OOP and refactor as early as it makes sense. It's a lot cheaper than doing it after everything is built.

尽可能早地考虑OOP和重构。一切都建好后,它比做它便宜很多。

#7


Write the main function minimally, with almost nothing in it. In most gui programs, sdl games programs, open gl, or anything with any kind of user interface at all, the main function should be nothing more than an event eating loop. It has to be, or there will always be long stretches of time where the computer seems unresponsive, and the operating system thinks considers maybe shutting it down because it's not responding to messages.

最低限度地编写主函数,几乎没有任何内容。在大多数gui程序,sdl游戏程序,open gl或任何具有任何类型用户界面的任何东西中,主要功能应该只是一个事件循环。它必须是,或者总是有很长一段时间,计算机似乎没有响应,并且操作系统认为可能会关闭它,因为它没有响应消息。

Once you get your main loop, quickly lock that down, only to be modified for bug fixes, not new functionality. This may just end up displacing the problem to another function, but having a monilithic function is rather difficult to do in an event based application anyway. You'll always need a million little event handlers.

一旦你得到你的主循环,快速锁定它,只修改错误修复,而不是新功能。这可能最终将问题转移到另一个函数,但无论如何,在基于事件的应用程序中具有monilithic函数是相当困难的。你总是需要一百万个小事件处理程序。

Maybe you have a monolithic class. I've done that. Mainly the way to deal with it is to try and keep a mental or physical map of dependencies, and note where there's ... let's say, perforations, fissures where a group of functions doesn't explicitly depend on any shared state or variables with other functions in the class. There you can spin that cluster of functions off into a new class. If it's really a huge class, and really tangled up, I'd call that a code smell. Think about redesigning such a thing to be less huge and interdependant.

也许你有一个单一的课程。我做到了。主要处理它的方法是尝试保持依赖关系的心理或物理映射,并注意哪里有... ...说,穿孔,裂缝,其中一组函数没有明确依赖于任何共享状态或变量与班上的其他功能。在那里,您可以将该功能集关闭到一个新类中。如果它真的是一个庞大的阶级,并且真的很纠结,我会称之为代码味道。考虑重新设计这样的东西,以减少巨大和相互依赖。

Another thing you can do is as you're coding, note that when a function grows to a size where it no longer fits on a single screen, it's probably too big, and at that point start thinking about how to break it down into multiple smaller functions.

您可以做的另一件事是在编码时,请注意当函数增长到不再适合单个屏幕的大小时,它可能太大了,并且在那时开始考虑如何将其分解为多个功能较小。

#8


Refactoring is a lot less scary if you have good tools to do it. I see you tagged your question as "C++" but the same goes for any language. Get an IDE where extracting and renaming methods, extracting variables, etc. is easy to do, and then learn how to use that IDE effectively. Then the "small, incremental refactorings" that Stefano Borini mentions will be less daunting.

如果你有很好的工具,那么重构就不那么可怕了。我看到你将你的问题标记为“C ++”,但任何语言都是如此。获取一个IDE,其中提取和重命名方法,提取变量等很容易,然后学习如何有效地使用该IDE。然后Stefano Borini提到的“小的,渐进的重构”将不那么令人生畏。

#9


Your approach isn't necessarily bad -- earlier more modular design might end up as over-engineering.

您的方法并不一定是坏的 - 早期的模块化设计可能最终会过度工程化。

You do need to refactor -- this is a fact of life. The question is when? Too late, and the refactoring is too big a task and too risk-prone. Too early, and it might be over-engineering. And, as time goes on, you will need to refactor again .. and again. This is just part of the natural life-cycle of software.

你确实需要重构 - 这是生活中的事实。问题是什么时候?太晚了,重构太重要了,太冒险了。太早了,它可能是过度工程化的。而且,随着时间的推移,你将需要再次重构。这只是软件自然生命周期的一部分。

The trick is to refactor soon, but not too soon. And frequently, but not too frequently. How soon and how frequently? That's why it's a art and not a science :)

诀窍很快就会重构,但不会太快。经常,但不是太频繁。多久和多久?这就是为什么它是一门艺术,而不是一门科学:)