In a recent post, Tom Beale argued that one of the central planks of good object design is a principle he called the FOPP (fundamental ontology property principle) principle:
As a fundamental principle, all properties defined on any given type in any ontology or information model must have the potential to be true for every instance of this particular type, at some point in its lifetime.
I think, after reflection, I’m going to have to disagree. And not just because of the name; it’s not that I don’t want to have foppish objects. No, it’s more than that. My first issue, which I made as a comment on Tom’s post is that the definition is still too loose to be of use.
Tom offered as an example, the property “wingspan” on a class of Animal; this, he claimed, clearly violated the principle. Well, ok, I guess that’s reasonable. So let’s be a bit more challenging. How about the property “legCount”? Is legCount a good one to be on the Animal Class? I’m not sure. I sure don’t think Tom’s rule helps me figure it out either. I’m going to start by interpreting as “apply” rather than “be true”. But still – does it have the potential to apply to all instances? Does a snake have 0 legs? Or is it an irrelevant concept on a snake? Does a kangaroo have 2 legs and 2 arms or 4 legs? I guess we’d need a good ontological definition of legs before we could figure that out. And, btw, we don’t even know what an instance of Animal is – is an instance of Animal a species, or an individual beast? I think that might influence the answer too.
I don’t know the answer, because I think it depends on what you’re trying to do.
And there’s the rub: it depends on what you’re trying to do.
Which is where it suddenly stops sounding so easy (not that it did). So let’s go back to nullFlavor, which is our favourite little poster child for this argument. Now Tom says that he’d like a Quantity class that is used for the following:
- A – uses Quantities to do statistical analysis;
- B – represents Quantities in data captured from user application screens or devices in an EHR system;
- C – uses Quantities to represent lab test reference range data, as found in a typical lab test handbook.
Tom claims that the concept of nullFlavor only applies to #B – because clearly you would never perform statistical analysis on data captured from users. Err. Right. Actually, I asked Tom about this and he said that you’d sanitize the data first before analysis (no, but that’s a separate discussion). But still, let’s grant that, and say that therefore we’ll take nullFlavor off this quantity type so you can define it and use in the hypothetical absence of unsureness.
But hang on – what do we do about use case #B now? Well, it pretty much comes down to two different approaches. You can take the HL7 v3 road, and define a “mixin”. That’s a class that extends it’s parameter type class. When I teach the v3 data types, the concept of a mixin is the single most difficult thing to cover. It’s such a simple concept to describe:
When we use type Quantity here, we’ll just add the nullFlavor to it
Easy to describe… but not at all easy to implement. I’ve never seen such a beast quite like that. AOP comes close, and C# has something close. Supposedly ADA does too – but I’ll never use that. The rest of us are stuck with real o-o languages where close enough is not good enough (anyone who’s stared cluelessly at an arcane compiler error about parameter type mismatches will know what I mean). In XML it comes naturally – but if you want to model the XML in some class language (UML, say), what are you going to do?
Alternatively, you can do what openEHR does, and wrap type A in a wrapper class that carries the nullFlavor. See, this is good because it avoids the mystifying questions a mix-in raises, and still means that
When we use type Quantity here, we’ll just add the nullFlavor to it by instead using a thingy that has the nullFlavor and then the Quantity.
All well and good. We now have an implementable solution based on this hypothetical ontologically clean class definition (though we’ve pretty much foregone being able to treat cases which don’t have a nullFlavor and cases which can’t have a nullFlavor interchangeably- this wrapper thing is in the way now). But the clean definition means nothing to me when I implement. Because I can’t go to my customers and say, “Oh, yes, this particular combination of circumstances isn’t something that could potentially be true all the time, so I can’t think about it.” No, indeed. My rule of thumb when programming is that something that happens one in a million cases will arise on a weekly basis across the deployed code base.
Given Tom’s design, instead of dealing with this cleanroom “Quantity” object, I’m just going to end up holding on to this wrapper class instead, so I can handle the few weird cases where I do positively need this nullFlavor. So much for clean design.
The problem comes from this “doing” thing: clean theory doesn’t make for clean implementations. All you can do is move the deck chairs around the Titanic (or see my second rule of interoperability).
p.s. A note about ISO 21090: there are attributes in there that violate the FOPP principle, and that I would’ve liked to go away (HXIT, for instance). But NullFlavor itself isn’t one of them. It’s ubiquitious presence is not an embarrassment to be subtracted away later; instead, it’s a positive statement of value. I understand why people (including Tom) resist that notion, but ISO 21090 does at least offer a single consistent solution to bad data across the whole implementation space. (Some of the particular nullflavors, on the other hand, clearly violate the FOPP principle, and mainly because of modeling laziness too).