Yesterday I made a somewhat unsubstantiated claim that you simply don’t need the Composite Application Block (CAB) to build nontrivial WinForms user interfaces in a maintainable fashion. I claimed that mere mortal developers can master the underlying design patterns in the CAB and pick up a better Inversion of Control/Dependency Injection tool to organically grow an application structure that meets the specific needs of that application. I even think that mere mortals can do this as or more efficiently as they can by utilizing the very generalized CAB. To try to substantiate that claim, and because there seemed to be enough interest to warrant this series, I’m going to convert my WinForms patterns talk from DevTeach into a series of blog posts to demonstrate some design strategies for creating maintainable WinForms code. Even if you’re going to stick to the CAB (and that is a valid choice), I can hopefully add to your ability to use the CAB by exploring the very same patterns you’ll find lurking underneath the covers in the CAB. If you like the way the CAB does something better than the ways I present, please write in and say so.
One of my favorite authors is Alexandre Dumas, author of the Three Musketeers books and the Count of Monte Cristo. The Three Musketeers was the soap opera of its day, written in installments as a serial in whatever passed for magazines in those days. Likewise, I’m going to try to dribble these posts out in little installments. Basically the size of post I can write in a single sitting on the train ride from and to work.
Note on terminology — I’m a card carrying Fowlbot, so I’m using the terms set forth in Martin’s forthcoming book on enterprise design patterns.
It’s just the User Interface right?
“Just” the user interface? I haven’t seen this so much lately, but earlier in my career there was a definite attitude that User Interface (UI) programming was simple, grunt work — unworthy of the attention of the serious developer. In my mind I always think of the line “Just pick it up” from Caddyshack, or Kramer extolling Jerry to just “write it off.” That’s a dangerous attitude because user interface code can easily be much more complex than the server side code in many systems. It’s worth spending some mental cycles on the design and structure of the UI code. Here’s why:
- Creating user interface code is time intensive. Not just in terms of the initial coding, but in my experience UI code generates the most defects and hence takes the most time to fix bugs. We can cut down the defects by extending unit testing as far into the user interface code as possible. TDD opponents often use the UI as an example of code that just can’t be tested. They’re wrong, but UI testability doesn’t come for free. It takes some care to craft a structure that makes UI code easier to test.
- User interface code interfaces with an extremely unpredictable external dependency — those wacky users! It takes a great deal of care to protect the system from garbage input and user error.
- The User interface changes frequently. Again, my experience is that it’s much easier for a user and analysts to get the backend logic requirements upfront than it is to specify the user interface. Making the UI code easier to change just makes sense, but that again takes some design cycles.
- UI code is largely event driven, and event driven code can be nasty to debug. Again, testability and a cleaner separation of concerns makes that debugging either go away or at least become easier.
A Note about Design Patterns
I know many people blow off design patterns as ivory tower twaddle and silly jargon, but I think they’re very important in regards to designing user interface code. Design patterns give us a common vocabulary that we can use in design discussions. A study of patterns opens up the accumulated wisdom of developers who have come before us. Finally, the design patterns we’re going to examine in this series give us a framework of ideas on the best way to divide responsibilities in our user interface code. That’s a good thing because…
User Interface code contains a lot of different responsibilities
It was almost comical, but sitting in the Agile track at DevTeach almost every speaker, including me, had at least one slide dedicated to the Single Responsibility Principle (SRP). Just to recap, the SRP states that any one class “should have only one reason to change.” Basically, you should strive for each class to contain a single coherent responsibility or concern. Like I said before, UI code can easily become complex. Just to prove that point, let’s list some of the responsibilities that could easily be contained in a single screen:
- The actual presentation to the user
- Capturing to user input events
- Input validation and business rules
- Authorization rules
- Screen flow and synchronization
- Interaction with the rest of the application
Lots of important things. Lots of things that would really be easier to code if I could just work on one thing at a time. Things I’d really like to be able to test in isolation from one another. Back to the Single Responsibility Principle, we can better enable change and improve maintainability in our UI code by assigning each of these responsibilities to distinct, loosely areas of the code. What we need is some sort of divide and conquer strategy for our WinForms code.
Of course, a large problem in WinForms development today is that the most common pattern of assigning responsibilities is…
The Autonomous View
You might not be running around saying “I’m building an application on the Autonomous View pattern,” but you’ll recognize it in a heartbeat. The autonomous view is a single piece of code that encapsulates both presentation and screen behavior in one single lump of code. In WinForms the autonomous view is characterized by completely self-contained Form and UserControl’s. While it’s perfectly suitable for simpler screens, it can present some maintainability issues for screens of even moderate complexity because of the mixture of concerns. The biggest issue for me is that the code in an autonomous view is significantly harder to unit test than code in POCO classes. It’s also potentially harder to understand because so many concerns are intermingled with the WinForms machinery. To some degree you can instantiate WinForms classes in memory and run unit tests against this code with or even without NUnitForms, but it’s often much more trouble than it’s worth.
To explain what I mean by the difficulty in testing, let’s look at this example..,
To be continued in Part 2: Humble Views