orthologonal - Properties and methods should not overlap in functionality.
If two methods do sort of the same thing but differently that
is a design issue. They should either do entirely different
things or there should only be one method. Providing two is
confusing and can potentially lead to bugs.
consistent - Naming of methods, properties and other entities should be
consistent. Example if you have a method "getColor" you should
not have a method "get_width" or "width" or any other variation.
You should stick with one format. In the same way argument orders
should be consistent. For example, if you have a method
"translateCoords(x, y)" you should not have a method
"setTopRight(y, x)". This principle is extensible into other
domains as well (such as exceptions).
composable - Methods and functions should be reasonably composable. Instead of
creating giant methods which do many things create small methods
which do one thing really well. Then provide a clear way to
compose methods together in order to accomplish a task. In this
way methods do not have to be written which explicitly perform
learnable - All design is for naught if the API is not well documented. Much
has been written on the importance of this step but it should be
stressed again. Good documentation can make up for any number of
short comings in the design. So document your api, provide
examples of how to accomplish tasks.
discoverable - Hand in hand with documentation comes the ability to be able to
naturally discover other parts of an API. This can be provided
in a number of ways based on language and platform. In Python
providing great dynamic help via the "help()" command and
docstrings is a good start. For web APIs consider providing an
"explain" API which returns documentation.
I'd also say that discoverability does not matter as much as documentation, IMO only very extensive APIs with lots of endpoints would actually benefit from it. I.e., in the case of web service APIs, don't waste resources on implementing complex nested endpoints with discoverability and HATEOAS when just a bit of clear documentation would do.
I don't mean to downplay your points; I agree wholeheartedly with your post, but I actually did a double take and thought I might have been spelling the word incorrectly for some time.
This simple statement just changed the way that I think about API's.
The difference now is that UX has become a popular, well-understood, discipline. So we have a lot more concepts to take back into API design.
Some of us have been capturing relevant links on the topic on a Developer Experience page (https://plus.google.com/116834904360889286443/posts) and under the #devexp hashtag. We don't have t-shirts, but we do have a logo in need of much love! I'll go add a link to that presentation too.
I wound up using Delphi.
The only thing I'd argue with is: "Use semantic objects for parameters".
I haven't had any trouble passing a null pointer and casting it. Your API is going to be expanding regularly and the more message-passing objects you create, the more message passing objects' definitions you'll have to expand as the API expands.
(It just struck me that this mirrors the approach Apple used often in Mac Toolbox: passing parameter structs with a version field, instead of modifying the API parameter list itself).
I also believe API should go through a feedback/review processes with peers. Other developers will have used different patterns, have a different base of experience with the platform, and can generally help find common mistakes. So, here's some of my feedback/nitpicks on MGTileMenuController.h :) --
@property (nonatomic, weak, readonly) id<MGTileMenuDelegate> delegate; // must be specified via initializer method.
The isVisible property looks like this:
@property (nonatomic, readonly) BOOL isVisible;
@property(nonatomic,readonly,getter=isVisible) BOOL visible;
// N.B. All of the following properties should be set BEFORE displaying the menu.
- (NSInteger)nextPageNumber:(NSInteger)currentPageNumber; // zero-based pageNumber
- (UIBezierPath *)_bezelPath;
In general, if the developer doesn't need the utilities, I highly question adding them to the header since you'll be responsible for them for all time.
Finally, why not add the delegate protocol to the header file so everything is consolidated for the developer using the class?
This is so, so true. If you find yourself thinking about exposing something because someone might have a use case for it or to provide greater control over some very specific corner case, write it down, file it somewhere, and revisit that when you see how people are actually using your API. Don't mistake all the ways your API could be used for all the ways people want to use it.
With the introduction of blocks in iOS 4, some APIs have shifted to using blocks for messaging. What is the general opinion on this?
Also, for components that benefit from an internal state machine - which is best practice to notify objects of a change in state? Delegate messaging, notifications, or just KVO?
As for notifying objects of state changes, all three methods are used. I use NSNotificationCenter if there will be multiple objects who may need to know about it, KVO as glue for the model and the view, and delegate messaging if there is only one object to notify.
Not providing a getter for that data means that if the client wants to use it elsewhere, it needs a second home. So now I'm keeping track of not just a HNCommentsView, but also an object holding the HNText and HNUser it's displaying.
Any time an API doesn't expose that data, there's an implicit assumption of, "The person using this class won't (or shouldn't) need to get at this data this way, so I can leave it out." Which makes it extra irritating to me when it's not there and I need it.
Some counterexamples that come to mind...
- [NSImage initWithPasteboard:] (must NSImage retain the original pasteboard and/or its contents for all eternity, in case you want it back, even if it was in some odd format like NSPICTImageRep that probably doesn't match the internal representation?)
- [NSDictionary initWithContentsOfFile:] (must NSDictionary remember the file path for eternity, and/or its contents, in case you want it back?)
Yes, if an initializer is basically accepting things that become properties anyway, it makes sense to expose those properties. But initializer arguments are not necessarily sensible or useful to keep around in all situations.