Precon Session: The Art of Writing a Reusable Class Library
The pre-con sessions started about 10 this morning and got out around 5:45 pm or so. There was a break for lunch, and then another one for the afternoon. I started to blog a bit at lunch for a “first impressions” of the session, but the power went out in much of Los Angeles (or so we are told) and the network link went down. Power was out about thirty minutes or so, and luckily happened during the end of the lunch time period so it didn’t cut into the sessions much.
This talk was given by Brad Abrams and Krysztof Cwalina. By sheer coincidence they also just came out with a book entitled “Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .Net Libraries”, and they gave out copies to the attendees of the session. They ran out of books as they apparently ordered just enough to cover the people that signed up for the session, but then they didn’t cross check the people coming in to see if they had signed up vs. just deciding to come to the session instead of the one they did sign up for.
The session was pretty good and I enjoyed it. This was the session I was really looking forward to. I’ve been pushing the design framework guidelines at my current client and have been using them for some time now. I also read Brad’s blog and was hoping to hear some real gems from this talk. What I got was pretty good.
The first hour was pretty much a rehash of the design guidelines Brad has published before and the previous design chats had covered, so there wasn’t much in that time that jumped out at me. The section was entitled “The Power of Sameness”, in which the idea is presented that the more your code is consistent within itself, and with the .Net Framework as a whole, that users of your libraries will more easily assimilate your libraries into their code and know what to expect from it.
One thing that did jump out at me was something he mentioned on overloaded methods. When you overload your methods you should have the less detailed versions of the overloads call the more detailed versions with “default” values for the parameters the overload doesn’t expose. This was something I was already doing and made sense, but then Brad expanded this to say the defaults should be things like 0 for an Int, 0.0 for double, "" for string, and false for boolean. He then gave a good example which is what caught my attention:
MethodInfo Type.GetMethod(string name)
//ignoreCase = false
MethodInfo Type.GetMethod(string name, boolean ignoreCase)
Originally the second parameter on the more detailed overload was caseSensitive, but if that was the case the default would have to be “true”, which is against the guidelines. They changed that to ignoreCase so that the default would match the guidelines. I wonder how many of my overloads are set up like this?
Brad also made the statement that there is fundamentally no difference between protected and public members when it comes to exposure in a class library. All anyone has to do is to inherit from the class to gain access to the protected member. Remember this when you are creating your libraries and make sure the internal keyword isn’t more applicable.
Krysztof then got up and presented the second session as “Framework Design Matters”. This was basically that upfront thought of how an API will be used and the key scenarios it should support will basically help keep the API easy to use for the end developer. He gave some examples of how they review their internal APIs. This included having two documents produced during the initial design: the Scenarios Examples and API specification. The Scenarios Examples are short bits of code that show how a developer would call your code. This makes you think about the simplicity you want to shoot for. A scenario example should be put together for each of the operations your library should be able to do that covers the 80% of its usage. The second document is the API Specification and this is just an drop of all the public methods and members the API will expose (no implementation code or comments; think about what you see when you use wincv and look at what a type exposes). These two documents are then reviewed as part of a API review before construction.
One of the little gems I took out of this section was that there are attributes that help framework developers show how their members show up in the intellisense (EditableBrowserAttribute) and how their objects display in the debugger (DebuggerAttribute). You can actually “hide” one of your least commonly used member from intellisense, though not sure why you really want to do this. You can also change how the debugger shows instances of your object in the watch windows and how a developer traverses the items in the object. Pretty slick.
Brad then got up and gave the next section of the talk called “Tools for Communication”. This was pretty much “how do I communicate with the end user the intention and features of this class library”. This included going over the when to use property vs. method, when to use public instance fields (yeah, don’t do that), and what you should do in your constructors. He stated that constructors should be lazy and usually just internalize the parameters given to it. For example, a constructor for a class that wraps an XML document should accept the XML data file name as a parameter to the constructor, but not load the file for you. The file load should occur when it needs to, not up front. The object may expose some other functionality that never even needs to touch the data. He did indicate that you should also provide constructors that provide the ability to do the heavy work up front as well, and let the end developer decide if he needs to call it. This also covered when things should be static and the introduction in C# of the static class identifier (which under the hood this creates an abstract class so it can’t be instantiated, and then enforces that all members be static).
The last section was called “The Pit of Success” and again Brad presented it. This all revolved around making it possible for an end developer to use your component correctly by making just as easy as falling into a pit. Or better stated, if your developer does the easiest thing, then they will be using it correctly. This section was pretty lengthy and I have a ton of notes on it. It covered exception handling, use of the “Try” pattern (aka, TryParse on double), the idea of removing features will increase the usability of your library, and a myriad of other things. Some simple snippets of this:
- Use abstract base classes over interfaces. They can be versioned better and you can add default implementation to members.
- Interfaces still have their place. They solve the multiple inheritance issue. But interfaces should be made to have limited members and are best used to describe behavior something should implement. This was pretty much what I already used them for, but the idea of limiting the size of your interface was somewhat new to me.
- Be very careful of using virtual methods. Brad used the phrase, “with great power comes great responsibility”. I like to word it, “your code may be used against you in a court of law”. What an end user implements with virtual methods is completely out of your control. Make sure that you provide a “contract” of what you intended the virtual method to be used for and if you would like the end user to call the base version. Of course, these are really guidelines as you can’t enforce them. Also, be very skeptical of any method you make virtual that your own code in the library will call.
- In 1.x if a worker thread in your application threw an exception it would bubble up to the top of that thread stack and then disappear. In 2.0 an unhandled exception from a thread will bubble up and take out your process. Hmmm, the jury is till out on if this is a good or not in my opinion, but it does provide for the rule that errors should be noticed that Brad put forth.
Okay, it’s close to time to get to a Birds of a Feather session and I still need to eat something. So, to summarize:
I really liked the talk. It had a lot of good content, but I think all of the content is also in the new book. The slide deck printed notes they handed out were out of sequence and many were missing…which apparently was a glitch of the event organizers and not on the speakers. The speakers did a good job of turning what was primarily a power point only presentation into something worth the time. They had several “Roll Play” scenarios where they played off each other to prove a point. They also had an exercise at the end of the session to produce a API design and you could send it in to them for review.
Great session guys!