A periodic republishing of habits that I would like to have. New items at the top, but each issue includes all prior items.
- Watch for Feature Envy
- Often we find that we’ve written a method that doesn’t refer much to the members of the class it’s in, and does refer to members or functions in one or more other classes. The code word for that is “feature envy”, and it’s meant to suggest that this code is trying to be a feature of one of the other classes.
It almost always pays off to create the suitable capability on the classes referred to. It’s usually as simple there, and often simpler. And the formerly envious code now does as it’s supposed to, dealing more with its owner class’s concerns.
- Data Over Procedure
- I like to lean just a bit in the direction of preferring data over procedure. There’s often a benefit to encoding information as data rather than as code. A table mapping product to product type, or synonym to base word, is often far more clear, more compact, and easier to modify than an
switch. Combined with “Wrap Collections” below, this approach can create very powerful transformations that are all nicely encapsulated. It doesn’t always apply, but when it does, the results seem to me to be quite nice.
- Wrap Collections
- I’m not really one for “always” and “never”, but it seems to me that wrapping native collections comes close to “always” helping and “never” hurting. It gives us a clear picture of what can be done with the collection, and what cannot. It allows us to change the underlying native implementation with a different, better one, at will. And it lets us provide a clear and useful interface to what is generally an over-powerful native structure.
- Wrap Native Objects
- I’m thinking primarily of numbers and strings, having addressed collections above. Wrapping numbers gives us a better chance of keeping the right number in the right place. In the Gilded Rose exercise, keeping
sellInis important. The units of
sellInare days. The units of
qualityare not. And
qualityhas very special rules applied to it. String manipulation is often very ad-hoc and can benefit from centralizing. And, like numbers, it’ all too easy to put the string representing the date into a slot intended for the last name. Isn’t that so, Chet Sep26?
I can’t think of any good reasons not to wrap native objects, especially collections, except that we think it will take us more time and we can “save time” if we put it off. In some cases, that may be true, but I think in general it saves time in the medium and long term, while not wrapping causes defects and longer debugging cycles.
On this one, and all these habits, they are my observations, things where I would prefer to develop habits that seem better to me. Your mileage may vary, and I’m not here to tell anyone what they should do. I offer these thoughts for consideration, and invite you to do your own experimentation with the ideas, to see what happens to you.
- Make Testing Easy
- I took the time the other day to make it possible to write some very similar tests with one line, calling a generic testing function. I found that I wrote more tests, because it was so easy, and the individual tests were very easy to read and understand.
- Fewer Assertions (per Test)
- I spent [DELETED] time a day or so ago, trying to figure out how a test result could possibly be what it was. I expected something like Expected 28, Got 30, but it said Expected 19, Got 20. Impossible … until I realized there were two assertions in the test and it was the first one, which I was sure would work, that was failing. Single-assertion tests are easier to understand, and point to the error when they fail.