Think your design is good?
Take the following test:
1. Take a class, any class and instantiate it in a test harness.
2. Find a coworker, let them read your code and tell you what it does.
3. Count the instances of duplication in your code.
Why take this test?
Number 1.
The ability to instantiate any class in a test harness demonstrates the level of decoupling and cohesion of your code. Why is that good? Highly decoupled cohesive code promotes: seperation of concerns, low duplication and extensiblity. You end up with only as much code as you need. You also get the power to duplicate and isolate customer found defects before fixing them, giving you a collection of tests you can rely on to prove the integrity of your code and design.
Number 2.
To often we write code that is write-once/read-never. Your code should be expressive of it’s intent and easy to read. It should do what it says and say what it does. Code spends more time being read and maintained than being originated. Additionally, when you’re writing highly decoupled code the more readable your code the more likely someone can come behind you and maintain/extend your work.
Number 3.
Duplication is the bane of maintainability. You (and I) have copy-and-pasted code from one unit to another to tweak it for some slightly different functionality. Stop it. Refactor that code into a superclass or into seperate object and use a subclass for specific functionality (see the Strategy Pattern). Duplication has more than one face: copy-and-paste (as I just talked about), and reinventing the wheel. On a larger team it easy to reinvent functionality that another team member wrote prior to you. Don’t be afraid to query your peers when you get that “someone must have done this before” feeling. You’ll reduce the complexity and size of your project by eliminating functional duplication.
How do you get there?
Check out:
- Eric Evans’s Domain Driven Design (don’t be put off by this rather large volume, it’s worth the read).
- Dan North’s thoughts on Behavior Driven Development - especially the precept of working from the outside in (which lines up with the “eat your own dog food” principle).
- Martin Fowler’s Refactoring (great book)
- Design Patterns - I really liked Head First Design Patterns by Eric Freeman, et al.
- Refactoring to Patterns by Joshua Kerievsky
So how did it go? Post your test experience in the comments.
Full Disclosure
I stood on the shoulders of the following giants to write this post:
Dave Astels
Michael Feathers
When refactoring code, always keep a sense of balance. Weigh up the amount of change the refactoring might introduce and reflect on whether these may be breaking changes.
Breaking changes?
1. Changing a single class to a class/superclass should ring alarm bells, because external code now has to access TWO classes. Did your original interface design take this into account?
2. Are people that use your class going through your interface as you intended? Will they now need to recode all their Class.Method calls into ClassA.Method/ClassB.Method?
3. Is someone using your original class in a way you hadn’t originally envisioned? Refactoring often introduces slight changes that shouldn’t matter, but the old code had some quirk that others now use, or have coded around, and the refactored code causes breaks that ripple through the project.
If you cannot answer these questions with certainty, copy/paste/modify is less aggressive and may be the right solution in your given situation.
OK, here goes -
1. Take a class, any class and instantiate it in a test harness.
Umm - I fell over at this point - what is a ‘test harness’?
Derek
PS - perhaps I need to find some shoulders to stand on
@Ken - This is why a robust automated test suite is so great. When you make an invasive change such as refactoring out functionality into separate classes you get immediate feedback on scope and efficacy of your change.
RE #1: There are patterns to help you deal with changing interfaces and method signatures. See the Proxy Pattern and the Adaptor Pattern.
RE #2: This sounds to me like it’s the same as #1. Correct me if I’m wrong.
RE #3: Automated test suite. You will know how this will affect other parts of the code when you can run your code in a test harness.
At the risk of repeating myself, when you have a robust automated test suite you *can* answer the above questions.
@jdawkins
RE #1 Patterns assume you can foresee what is coming and that what you have selected will still hold in the future. If you have this level assuredness, why use TDD/Agile, a methodology based on the assumption of constant change? Do you see the conflict?
RE #2 The first sentence asks whether you are certain that the way you designed your class is in fact the way people are using it. Is someone using just a few calls, and after you refactor, that subset now needs, say, initialization which now is sitting in the other bit? (yes, the second sentence probably make more sense under #1)
RE #3 You are already letting your chosen methodology run on ahead.
When you have a robust automated test suite, with all the time and effort invested in this parallel structure, you have already committed yourself to a particular direction and it becomes financially and psychologically difficult to motivate changes that go beyond the superficial.
If you have the time and staff to over-engineer in this way, well and good. But as I said, keep a balance. Step back. Think first.
@DerekSmith
what is a ‘test harness’? If you are using Delphi then a ‘recommended test harness would be DUnit. The test harness is a separate project that provides a framework to efficently code tests for units or classes that include to your ‘customer’ project. So the end result is that the code you write is used by two projects or applications: the one you release to customers and your internal testing app.
@jdawkins
another great post… it is amazing how much ‘fear’ cripples change. TDD helps define the needed interface and sets the contract of that interface’s behavior (that matters). That gives the freedom for changes without fear because an interface under test enforces the legacy contract and allows for added behavior with minimal fear of braking legacy apps. keeps pushing the issues…
@Ken
Patterns -*do not*- assume you can see what is forth coming. Patterns are not something with which you start. I don’t start coding with a pattern in mind. This is why I recommend reading Refactoring to Patterns. A pattern is something you use to solve a problem - you have to have a problem first.
A robust test suite is *not* something that requires time and dedicated staff as you’ve mentioned you have in your shop. When you write the few lines of test code first then fill in the production code you slowly grow a comprehensive test suite without having to dedicate time and additional manpower. Tests written after production code are considerably more costly in terms of time and manpower than those written up front.
I recommend both Refactoring to Patterns and Head First Design Patterns for a complete understanding on how these activities cohesively work together.
Hi,
I really appreciate this post, because it’s really an impressive work. You provide useful information.
Really it provided me some unknown information and sure I accept that in reading blogs helps us to gather some good information for all the topics which improves our knowledge. Thank you.
I was so happy to find your page exactly one day before the borderline of my essays and custom term paper. I know that you are a well-recognized and honored source on the Internet.
Take a class, any class and instantiate it in a test harness.
Umm - I fell over at this point - what is a ‘test harness’? cool
I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I’ll be subscribing to your feed and I hope you post again soon.
An amazing machine, you deserve to 1. These fantastic shoes can be a brilliant and attractive. Enjoy and of itself.
I found your website perfect for my needs. It contains wonderful and helpful posts. I have read most of them and got a lot from them.
It is good to see you verbalise from the heart and your clarity on this important subject can be easily observed. Thanks again!