- too specific (hard-wired to a particular client),
- too generic (hard to use for simple problems),
- not extensible (adding functionality requires changing the interface), and
- simply to darn hard to understand.
Consider a piece of code that parses a file where each line holds either a number or a string of text. How should the interface to this code look? Some alternatives are:
- bool str_to_int_or_str(const char* content, int* int_value, const char** text);
- OneOf<const char*, int> str_to_int_or_str(const char* content);
- void read_line(const char* content, void (int_func*)(int), void (txt_func*)(const char*));
- void parse(const char* content, Visitor* visitor);
- template <class C, class V> parse(C content, V visitor);
With small pieces of code like the example above, it might not be clear why thinking about modeling is important. But consider designing an API for a large and complicated library. What are the concepts that the client need to understand? What are the actions it can perform? What side-effects are expected and when should they be visible?
At work I'm developing an application that just-in-time compiles a proprietary language into native x64/x86 code. We've had some bugs related to when variables are updated. This probably sounds like trivial things to get right, but when you get deep down in the details these things easily slip through because your brain are occupied thinking about other aspects of the problem. These bugs could have been avoided if there was a layer that took care of these details. That is, a layer that models these inherent properties of the language and provides an interface that helps the developers to think in terms of the model.
This brings me to the other aspect of modeling -- as tool for communication.
Given any piece of code -- two programmers will think about it slightly different. Furthermore, the larger the code, the more likely that two programmers will understand it more different. In other words, it will be harder for them to understand each other when talking about the code. However, if the code is partitioned into smaller parts and each part models (not only implements) some logical sub functionality, it will be easier to understand it.
Note that the important word in the above paragraph is not implements, it's models. The implementation of an interface is irrelevant for understanding what a client does if the interface models the solution properly.
Think about opening and reading files. In most languages it's as straight-forward to implement it as it is to say "think about opening and reading files". The interfaces models the way we think about files. Great! That means we don't need to understand what open does in order to understand what the code using open does.
This means that one easy way to get an interface to be easy to use is design it by mimicking something other people already understand (e.g., a parser, a visitor, a file). This is the alternative I've taken the last few times I've designed an interface -- trying to find a suitable analogy and use that as model.
No comments:
Post a Comment