I find in business applications that its important and useful to think of variations-on-a-theme as modeling a buffet restaurant: pick and choose the features (items) that you want. Generally one wants to separate the implementation of features from the encoding of them (data). Paradigms or techniques that try to closely associate the implementation with the feature encoding itself generally have scalability or maintainability problems.
Differing ways to encode them include:
- Tables with Boolean switches or indicators. Here's an example Product table:
feature_A // Boolean
feature_B // Boolean
feature_C // Boolean
(Note: in practice columns would have a more descriptive
names. "Feature" is merely a stand-in.)
- Many-to-Many feature tables:
productRef // foreign key to productID in Products
featureRef // foreign key to featureID in Features
- Associative arrays or object attributes:
p = newArray();
p["feature_b"] = true;
p["feature_f"] = true;
// non-mentioned features are assumed false (or per-feature default)
doSomething(productID, feature_B=true, feature_F=true);
// non-selected features are off or have defaults, based on convention
If not supported, I sometimes emulate KeywordParameters?
with descriptive flags inside of parenthesis:
The parenthesis approach is easy to parse in many languages, including SQL. See also EmulateKeywordAndDefaultParameters
. Keep in mind that features are not necessarily Boolean. For example, text alignment may be "left", "center", or "right". These are trickier to emulate with the parenthesis approch, but one can use indicators such as "(align_left)".
The real problem/danger is handling the case where some combinations of features are incompatible or remarkably more expensive than other combinations, especially with parameterized features (e.g. (engine_power(140+ hp)) vs. (engine_140_hp)). Given 20 boolean features, it would take a million database entries to describe each combo. Given non-boolean features, and that number can explode. In practice, I usually see people offering 'packages' - products with groups of features and at most a few optional modifiers. PackageOrientedProgramming? It leads to fewer tests and fewer surprises (e.g. errors leaking through).
I'm not sure what you mean as far as efficiency. Regarding validation
, it is not an easy issue for larger organizations and I've seen people pull their hair out trying to get validation strict enough to keep out the wrong stuff but not prevent the right stuff that they could not forsee up-front. In fact, I lean toward using night-batched "Suspicious Data Reports" that flag stuff that may need a closer look. All-or-nothing rejection tends to be a blunt instrument and difficult to get right. I don't think there is any magic paradigm to solve it. Ways to simplify it is to build classification systems for the features (the features have features!) so that groups can be wild-carded in the validation logic. A ControlTable
that has validation formulas (such as WHERE clauses) on top of table views can also make it easier for non-programmer power-users (business analysts) to manage many of the scenarios, with the programmer dealing with those too complex for the expression-based control-table.
I didn't say anything regarding 'efficiency'; a million entries becomes, rather, a management problem. I agree that there's no magic solutions here. Call it EssentialComplexity: when there are 20 boolean options, there are (at least) 2^20 combinations, and that's ignoring 'don't care' (apathy) and 'don't know' (ambiguity), which bring one up to 4^20 combinations. These features might interact in complex ways... exhibit EmergentBehavior. No paradigm, no matter how nice, can make that problem go away (though ConstraintAndLogicProgramming can help you by automating choice of some features based upon desired results - i.e. it hands complexity over to the language implementation). Of course, that doesn't prevent one from taking FeatureBuffetModel a little further and turn it into its own 'orientation' (FeatureOrientedProgramming - can't beat an acronym like "FOP" ;-) :
do with action=something, productId=x, featureB, featureF, -featureL, *featureW, ?featureZ.
Taken far enough and you'll end up with a TupleSpace language, or possibly a BlackboardMetaphor. In a sense, your 'doSomething()' above was already a 'package' with certain offered features.
As to whether nightly batches make sense, that depends on how immediate is the effect of 'doSomething'. If it's an "I can get back to you with an e-mail later" issue, the nightly batches work well enough. If not...
As a consumer (of both goods and API's), personally I dislike bundled deals. I like to pick and choose the features I want. But I understand there is often economy of scale in bundling features. But even if a company wants to bundle features, the methods I describe do not stop that and allow flexible re-combinations of bundles. If features are bundled, it simplifies testing and validation here also, as certain combo dimensions "collapse" into consistency. In my experience, bundled deals change also. Marketers are forever tinkerers and keep re-bundling trying to con consumers into the same old crap with a different name and package. Thus, hard-wiring groupings into code is a code-maintenance no-no.
As a consumer (of both goods and API's), personally I want a magic wand that I wave and I get whatever it is I really want at the time. But I understand that reality often interferes. ;-)
The method you describe creates a bundle called "doSomething" which has various options which, themselves, are bundles. "doSomething" itself is a hard-wired grouping and therefore, by your description, a code-maintenance no-no. Further, the individual "features" themselves say yes/no to large 'abstractions'... e.g. "featureF" is actually a bundle, too. You can't, fundamentally, escape bundling... as with the magic wand, reality interferes. But I understand the desire to keep features as orthogonal as is possible and practical. Such an approach is pushed in TransparencyAndUniformity, too.
I am only suggesting it is more flexible and more bundle-free than the known practical alternatives. I never claimed it was 100% bundle-free. --top
But it isn't "more bundle free" than the alternatives. Your choice, like all alternatives, is 0% "bundle free". It does offer a strategy for selecting bundles that I do tend to believe is more 'flexible' (though that flexibility has its own costs - combinatorial explosion, for example). Good design, even in FeatureBuffetModel, will still ultimately come down to careful choice of the 'bundles' users may wish to use, whether you implement them as 'features' or 'procedures' or 'classes'.
The assumption here is that domain-space bundling of features is fleating or unpredictable. If your observations are different, then so be it. I build systems based on my observations and you do the same. I find that if the system ofters a more mix-and-match, that users grow to like the flexibility and are pleasently surprised. True, sometimes pre-digested bundles are offered for newbies, but this will not necessarily prevent the offering of mix-and-match also. And.
I don't know what you mean by it causing a CombinatorialExplosion
. The combos are specified in data tables, not in code. It is hard to them compress further than this without forced bundling (such as subtyping). It is like a player-piano
: the table(s) is the paper "list" of notes, and the app simply plays what is requested. For those of you who've never seen a player-piano roll, it looks something like this:
CCDDEEFFGGAABBCCCCDD..etc.. <--- note letter
.#.#.#.#.#.#.#.#.#.#. <--- sharp indicator
111111111111112222222 <--- octive number
Each column is a note on the piano, and the vertical bars (pipes) represent played notes. The dots are just place-holders to prevent TabMunging
. The roll is slowly rolled over the reader of the holes. Thus, one row represents an instance in time. (I have to clean the sharps out.) In this diagram, horizontal is frequency slot, and vertical is time. The roll itself is roughly the width of regular office paper, although may vary per model. (Some electronic music software uses a virtual form of these.)
Perhaps you meant a combitorial explosion of potential
options, such as potential test cases. That may be true, but if they exist in the potential problem space (domain), then they exist. Perhaps you are suggesting not offering choices in order to reduce testing. That's more of a political/management decision point. I generally take pride in offering flexible systems that make users happy.
Another benefit outside of providing many options to the user is to address the situation where the customer is not sure what he/she wants
and you have to produce something soon. It's easier to change flags in a table than change code, partly because non-programming "power users" can help with that task.
I'm hardly damning FeatureBuffetModel. I'm simply noting that even the things you're calling "featureX" and "featureZ" and "doSomething" are bundles. You aren't escaping the need to carefully choose 'bundles'... e.g. choosing whether "featureX" should be a single feature or itself broken into two or three component-features or merged into some other bundle. You'll never escape that requirement. And since you can't escape such bundling, you should view FeatureBuffetModel only as a strategy. As a strategy, FeatureBuffetModel makes a trade between flexibility and combinatorial explosion of implementation, testing, and (where some combinations of features are impractical or impossible) validation effort. I'm not even saying it's a bad strategy; I've used it myself to good effect. But, as with all strategies, you ought to be very aware of exactly what you're buying and what you're paying to get it.
It would be interesting to compare it to other techniques and see what the trade-offs are. That is what this wiki is all about. Dealing with combinations that are not practical or logically impossible are going to occur regardless of the technique used to manage them. In many cases, tablizing the features allows one to study the combinations easier because one can use off-the-shelf data, reporting, and query tools to analyze and sift such tables every which way. Sure, that may not be sufficient for all checking, but it's a big help.
See Also: TransparencyAndUniformity