Every now again, I hear of a wacky design idea or a challenge to a deeply held opinion on design.
I like to keep a little list of wacky design ideas that I have read about and every time I start a new project, I'll pick one of the ideas to try out on that project. It's surprising how often the wacky idea turns out to be good after all.
An example of a rule that has worked well for me is Don't use checked exceptions.
Other rules that have been more qualified successes are
Code to the happy path
I don't put so much effort into checking parameters any more (exceptions: when the method is part of a public API or when the resulting error would not be immediate).
Don't use JDK types in your API
Your API should only include types that are relevant to the problem domain.
Here are some that I am currently trying :
Names of test methods should always include the word "should"
This is inspired by Behavior Driven Design, the idea being that it forces you to think more about the responsibilities of the object you are testing.
Make fields public
If there is no reason to hide a field behind a getter/setter then don't.
Don't expose an object's state
Not through public fields. Not through getters. Tell, don't ask.
Instead of having an ever-growing set of static assertXXXX() methods, have a single assertThat() method that takes an actual value and a constraint. With suitable sugar-methods, you can make your assertions read almost like english :
assertThat(myFile, contains("this string));
Posted by Kevin Lawrence at November 2, 2005 11:10 AM
TrackBack URL for this entry:
As an opposite to "Make fields public", you might want to try full self-encapsulation, http://www.rolemodelsoftware.com/moreAboutUs/publications/articles/self-enc.php
Posted by: Jason Yip on November 2, 2005 05:06 PM
Abstract State (and all the other patterns which depend on it) was my previous default design. But now that tool support makes it trivially easy to Encapsulate Field at any point in the future, I go for the simplest thing - which is to leave the field public.
Posted by: Kevin Lawrence on November 3, 2005 07:55 AM
When might you choose one approach over the other? That is, when is "Make fields public" a good or bad idea? When is full self-encapsulation a good or bad idea?
Posted by: Jared on November 8, 2005 05:40 PM
[with the important caveat that these rules do not apply to published APIs] this is a typically evolution for the internal state of a new class :
When a new class comes into being, I'll make all of its fields private - total encapsulation.
As I add new behavior, I try to follow the "tell, don't ask principle" and keep the internal state private whenever possible.
Sometimes, though, the state really does need to be made available to other classes. In that case, I'll just make the field public.
If I discover that the state can be derived from other fields (like the diameter in the Abstract State pattern that Jason pointed us to) or if subclasses will have a different notion of what constitutes the value, I'll just Encapsulate Field and use the accessor method everywhere (inside and outside the class).
Ken Auer's paper implies that once you have committed to a particular data structure, it will be hard to change later, but modern refactoring tools make it trivial to change your mind later so it's possible to always do the simplest thing until you need something more flexible.
Posted by: Kevin Lawrence on November 9, 2005 08:03 AM
"Names of test methods should always include the word "should"...forces you to think more about the responsibilities of the object you are testing."
Yes. Separating "should" from statements about the problem, or problems with the solution, is so important, it's the basis of the issue/position/argument form that's so central to sorting out political 'shoulds'. If it can handle those messy problems, it can handle yours.
"Make fields public" vs. "Don't expose an object's state" can be best resolved by esuring that you have no objects that are so passive that they can work with fields exposed. Refactor. And do all your "setting" with assertions, in other words:
"...have a single assertThat() method that takes an actual value and a constraint. With suitable sugar-methods, you can make your assertions read almost like english :
assertThat(myFile, contains("this string));"
This is exactly the principle applied in the living ontology, that active verbs (from the perspective of the user or using service) ought to be strictly controlled as to their meaning. There's one list of all control verbs paralleling the list of all issues debated. The "should" language is restricted to positions, and assertions about testable behaviour exactly as you describe, i.e. many "position:" namespace articles have a "should" either explicit or implied in their title.
This restriction of verbs is the same principle as REST and standardized user interface design, which rely on a very few verbs ("preview", "post"). You might be familiar with it in dating services where such self-assertions as "I'm a ___ age __ in __ seeking a ___" are common. When I designed the predecessor to the lavalife service, reducing all the language to simple assertions with common verbs was the main goal, and it worked very well: the instructions read similarly to the screens, and so on. What's harder is to make errors and exceptions also converge to this language:
Bill Joy considers the convergence of error messages to the architectural objects to be the number one sign of good operating system design, and I agree.
How anyone thinks they can make user interfaces more natural for users to read, but also
Posted by: Craig Hubley on December 3, 2005 09:59 AM