A Dangerous Obsession with Primitives

  • This is what refactoring is for. Start out simple (maybe a primitive is all you need) and then, if and when the need arises, refactor and introduce a value object. That's where the failure is in this story.

    I've seen a lot of premature abstraculation blow up the opposite way: incomprehensible, elaborate object models attempting to be The Best Abstraction Evar around fundamentally simple concepts. These things can be hard to work with and change, contribute difficult to discern complexity to applications and cause huge performance headaches.

    You aren't gonna need it, worse is better, simplicity of implementation above all else.

  • I don't see the problem with primitives here. Realistically, the alternatives are either to store a standardized float, or a struct that contains a float and a type value. Floats have issues themselves with accuracy that need to be accounted for, so it's pretty easy to see why they tend to be avoided.

    Internally, you want a standard set of units. It doesn't matter what they are: grams, bread units, etc. It only matters that they're standard across the application. That reduces the complexity of the actual functionality, which is presumably totaling up the information and comparing it to whatever limits the user inputs. You don't want to have to switch all your internal logic based on user input type, you want to convert user inputs to a standard field type and work from there. Honestly, different calculations based on multiple unit types is as big a code smell as converting the input data.

    So realistically, it's the same effort no matter what input type the user has: Either you're converting it at the base, converting it before you do your calculations, or writing several specific cases for each input type. It's the same number of test cases, too.

  • I'm reminded of Bjarne Stroustrup's keynote "C++11 Style" that describes C++11's vision of "Type-rich Programming."

    Slides: http://ecn.channel9.msdn.com/events/GoingNative12/GN12Cpp11S... -- "Type-rich Programming" comes in at slide 17.

    Video (and Audio downloads): http://channel9.msdn.com/Events/GoingNative/GoingNative-2012... -- slide 17 comes in around a fifth of the way in, as the player does not appear to have minutes/seconds indicator. Nonetheless, one should be able to find their way using the slides (pdf) images in conjunction with the video to find their spot.

    I don't have time to review the intro into those slides, if they are relevant, but the keynote is generally good anyhow, so I'd recommend viewing the whole piece.

  • Always use base SI units, no matter what the locals do. Convert at the point of input and output.

  • This sounds like the kind of problem that puts really expensive robots into craters.

    http://youtu.be/OB-bdWKwXsU

    There's a salient point in that talk where he describes a mission that failed due to a human error. Someone forgot to convert an internal representation of a unit in some algorithm which led to a miscalculation and a loss of some millions of dollars. Apparently the error made it through all of the code reviews and tests that were written. Bjarne then shows some pretty simple code that might have avoided such a disaster by specifying your types up front and letting the compiler do the work of converting them. Seems like a pretty simple concept and I wonder why it's so often missed.

  • The solution is actually worse than the initial problem. Unit conversion is a more complicated issue. The solution introduces all sorts of horrid conversions and rounding issues.

    Consider a third of an inch. It can't be represented accurately in decimal. The same is true with other units.

    You need to store in the source format which may vary based on the type of unit. As a rule, I tend to keep convertable unit values as ratios of two bignums with the unit attached as an enumeration. These can be represented and manipulated as value types (structs) in c#.

  • I can't even count the number of times I've seen the exact opposite. Types that were so over engineered that a simple int would have sufficed.

  • I also don't quite see how a "value object" would helped (or much differed) there?

  • Of course, if you use F# you can just have units of measure on your types...