Blog on Dan North & Associates Limited 10月02日 20:57
代码示例应避免过度遵循DRY原则
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

文章讨论了在软件开发中,尤其是编写示例和测试时,过度遵循DRY(Don't Repeat Yourself)原则的潜在问题。作者认为,虽然DRY原则在代码重构和一致性方面有其价值,但在示例和测试中,清晰性和叙事性更为重要。通过将辅助代码内联在测试中,可以使测试代码更流畅,像讲故事一样揭示代码的功能和意图,从而提高可读性和可维护性。

📚 文章强调在编写测试和示例时,清晰性和叙事性比过度遵循DRY原则更重要。示例和测试应该像故事一样,逐步揭示代码的功能和意图,而不是简单地避免重复。

🔄 作者指出,在测试中过度遵循DRY原则可能导致代码结构混乱,使测试难以理解。通过将辅助代码内联在测试中,可以使测试代码更流畅,更易于阅读和维护。

🔍 文章建议避免使用before blocks或setUp方法,直接在测试中调用设置方法。这样可以避免让读者猜测是否存在其他类中调用的魔法方法,使测试意图更明确。

🖊️ 作者引用了Martin Fowler的配对编程经验,说明将辅助代码内联在测试中可以使测试代码像故事一样展开,提高可读性。

🚫 文章还提到了过度遵循DRY原则的潜在问题,例如可能导致代码库中存在不一致的定义,增加维护难度。

Should examples/tests/specs/whatever be DRY (Don’t Repeat Yourself)? I’ve been thinking (and talking and arguing) about the value of test names recently and whether they are just unnecessary duplication, but that’s the subject of a future discussion. This is about the actual content of your examples. So, should your examples be DRY?

In a word, no. DRY zealotry is a classic example of an over-adherence to Best Practices, which as James Bach argues are a bogus concept anyway. When you are writing software, including executable examples, your focus should be on clarity of intent. If the code could be any more obvious, you’re probably not done yet. And given that code is created once but read and changed many more times, it is healthy to develop a strong sense of responsibility to the guys coming along after you.

In tests, flow trumps DRY#

The DRY principle says that the definition of any concept should appear once and only once in your code. This is an admirable aim in that if you have to change the behaviour of a Flooble, you want to be able to change it in one place and be reasonably confident that your change will apply consistently across the codebase. If there are multiple definitions of a Flooble, the chances are that not only will you not catch them all, but that someone before you didn’t catch them all and the multiple definitions are already inconsistent, and who wants that?

However, when you are using examples to drive your code - a process I call Coding By Example but which most other people insist on calling Test-Driven Development - there is another principle in play that I believe trumps the DRY principle. The examples tell a story about what the code does. They are the documentation narrative that will guide future programmers (including yourself when you come back to change this code in three months time and you’ve forgotten what it does). In this case, clarity of intent is found in the quality of the narrative, not necessarily in minimizing duplication.

Some years ago I had my first experience of pair programming with Martin Fowler. That is, I had done quite a bit of pair programming, just not with Martin before. We were looking at some ruby code I had written test-first, and Martin asked to see the tests, “to find out what the code does”. Then he did a rather odd thing. He started moving the tests around. I had a few helper classes and utility methods in the source file, neatly at the end out of the way. He moved them up and dropped them inline just ahead of the first test that used them.

Madness! I thought - now the supporting code is all over the place! It really offended my sense of tidiness. But then I saw a pattern beginning to emerge. The test code was starting to read like a story. He would introduce these little methods and classes just before their one walk-on line in the narrative. It was quite an eye-opener for me. The test code flowed, and unfolded the story of the class under test. (I know I’m using TDD vocabulary - this was in my pre-BDD days.)

Go with the flow#

The A-ha! moment for me was when I imagined reading a story book where the plot and characters had been DRYed out. Everything would be in footnotes or appendices. All the character descriptions, plot elements, subtexts etc. would be carefully extracted into fully cross-referenced paragraphs. Great if you are “reading” an encyclopaedia, not so appropriate if you want to get into the flow and find out what happens. You would be forever flicking back and forth in the book and you would very quickly forget where you even were in the story. In the words of the old joke, a dictionary has a lousy plot but at least they explain all the words as they go.

So here’s my challenge to you. When you read through a test case, or spec file, does the story unfold? Does it start by introducing the object’s most important responsibilty? Does it then introduce the edge cases in descending order of priority? If the test uses helper classes and methods are they tucked away at the end, or worse yet in an entirely different file that I am expected to “just know” is there?

Try to avoid using before blocks or setUp methods - especially in an abstract test class. Just call the method that does the setting up directly from the example. Don’t leave me to guess there might a magic method in a different class that is being invoked before the test even runs - that’s just not fair.


Also thanks to Mikel Lindsaar from the rspec mailing list for an excellent article about the perils of slavishly following DRYness.

Check out

Goalwards®

, our new business agility practice!

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

DRY原则 测试驱动开发 代码示例 清晰性 叙事性
相关文章