views:

181

answers:

3

When writing a library or the public API of a module that will be used by a lot of other code in a variety of use cases, what is the best way to balance flexibility with ease of use? I believe that the two often conflict in that, the more flexible you make something, the harder it is to get it to do any one particular thing well.

For example, the C++ STL uses iterators, which IMHO are horribly low level and annoying to work with, but in exchange they are extremely flexible in allowing the same code to operate on all kinds of STL containers. Another example is the design philosophy of the Java standard library, with its small, very specific classes that are designed for maximum modularity and flexibility, versus the Python standard library, with its preference for a flatter class hierarchy that makes handling the common use cases simpler. How should things like these be balanced?

+2  A: 

I think you need to consider the target audience for the library - if you're writing a library that less experienced developers may well use, you have to give thought to how to help them. In the case of the C++ STL probably most developers that are using them do not mind the extra mechanics because they are used to them and value the flexibility much more.

You may want to think about two tiers of access through your API, that has a level that keeps things simple and a layer that allows for more control. But you may want to see how the framework develops first before you go to that length.

Kendall Helmstetter Gelner
A: 

I'm fond of the .NET base class library API. The design of the library most follows these guidelines.

While reading these, and the accompanying book, I took several key pieces of knowledge:

  1. The API was designed keeping in mind that modern editors have intellisense capabilities. Longer names are acceptable because you can tab-line complete class and method names. This is in opposition to C-style functions with shorter, obfuscated names like strnicmp()

  2. Use of the Create, Set, Call pattern: Always have a default, no parameter constructor. Provide properties which can be set in any order, and then allow methods to be called. Using this pattern allows an object to be in an invalid state for a short period of time. (After the constructor was called, but before any properties were set) But this is okay. You can communicate API misuse by throwing exceptions. This makes using the class more approachable.

Matt Brunell
This is certainly a subjective thing, though: I find the .NET APIs horrid. I prefer a functional-style APIs with mostly immutable types and free (or static) functions that operate on them. Less state = reduced coupling, fewer threading issues, easier-to-read code. But this is a matter of opinion.
Robert Fraser
+2  A: 

If you are part of a standards body that can enforce usage of your classes on others then you can go with flexible and complicated (e.g. stl).

For everyone else, unless there are some really compelling reasons, then ease of use should always be your first choice. Otherwise, few people will use your code/API. If the learning curve to use someone else's code is high then most people will opt to reimplement just the parts they need. That is usually way quicker and problems are easier to resolve.

In my opinion, "ease of understandability" is 2nd only to "It Works Correctly" when it comes to rating the quality of code.

So bottom line, if adding flexibility comes at expense of easy to learn and use then don't add the flexibility until it is PROVEN that the flexibility is necessary.

Dunk