Friday, July 24, 2009

Build Your Own CAB: Introduction

Jeremy Miller was working on a "Build Your Own CAB" series a while back. However, he does not have an intimate familiarity with CAB. I have spent 2.5 years working on a CAB-based application, with some side projects that use Castle Windsor.

Currently, I'm starting on a GUI project using Mono on Linux. It's against the CAB license agreement to use it on Linux, so I won't be using it. Instead, I'll be taking a look at the components of CAB, how to reimplement them or what replacements are readily available, the merits and deficiencies of CAB's implementation, and ways to avoid potential problems.

CAB Components


CAB consists of several components which could in theory be used separately. In practice, CAB's internal design is too scary to contemplate swapping out components, so the best you could hope for is to rip out part to use elsewhere.

Service locator


Technically, it's dependency injection, but I generally expect a dependency injection system to figure out the correct order in which to build things.

CAB's dependency injection system encourages you to have god classes that add a few hundred services to the workitem. (A WorkItem is like a Windsor container; it holds a collection of services and objects built, and handles events.) This is very procedural and does not involve actual inversion of control, as such; but it looks enough like using a full-fledged IoC container that it's reasonable to switch.

Events


CAB events are wonderful. Somewhat. They closely match Ayende's EventBroker, except for being based around EventHandler rather than Action (that is, Ayende decided his events shouldn't ever have parameters, whereas CAB decided on [object, EventArgs]).

There are some advantages to the CAB system, but there are a couple of maintenance issues as well, and a few handy features that it entirely lacks support for. On the whole, CAB events are great.

Commands


Commands are events that can be turned on or off. Not much to say about them other than that.

Items


Items are, well, any state you want to keep around. It's a place to build transient objects or store items temporarily.

I suggest only using it for building transient objects. You don't want arbitrary classes pulling stuff directly out of the workitem, since that's unnecessary coupling, so you may as well use a service with local variables instead.

Modules


A Module is just a DLL with some bootstrapping code. A CAB application will contain an XML catalog of modules that will be loaded and run on startup.

CAB Organization


In CAB, everything is part of a WorkItem. The intent is that you have a root WorkItem that represents the essential, system-wide state of your application, and you create child WorkItems on a per-task basis.

In essence, CAB uses WorkItems as a lifecycle mechanism for components. In CAB, configuration is tied to initialization. This means that creating a child WorkItem requires you to call the necessary configuration code when creating the WorkItem. This code can be relatively verbose, and this leads to the impression that CAB is slow. (Additionally, since CAB uses reflection extensively and, due to the marriage of configuration and initialization, unable to cache anything, CAB is slow. However, your IoC container will almost never be a significant bottleneck.)

In practice, child WorkItems will be used rarely. Only if you know you'll have a whole slew of items that you need to create now and destroy later -- for instance, creating a new and complex window that shares little functionality with the rest of your application-- will you resort to child workitems.

Conclusion


I've talked a bit about what CAB is. Next time, I'll discuss how a CAB application is structured, with special reference to the common pain points.

No comments: