Edited by Nina Miller
Figure 1 - Spaghetti Code in Action
On my last two projects I encountered a common design nightmare. The problem to which I am referring is a growing epidemic, familiar to all consultants who take on program applications previously developed by junior software developers. The problem is that the application logic is embedded inside of the GUI code. Code is designed in this faulty manner is the by-product of inexperienced software designers who believe they can simply skip the design phase of a project with no repercussions.. Bad code also results from using the RAD features in Visual Studio as a crutch to do everything conceivable in the GUI. The outcome? A complete and utter mess of undecipherable code! How often is this coding flaw seen in the industry? More times than you'd ever want to imagine.
Why is Putting Application Logic in the GUI bad?
Some junior programmers reading this article may be asking themselves this question. I certainly asked the question in my early days of programming. If I hear the question now, I cringe. Putting application logic in your GUI is a recipe for disaster for the following reasons:
1) The code becomes unreadable.
2) There is a tendency to compound cohesion when lumping application logic into GUI logic.
3) Combinatorial problems pop up from the many inputs being tracked against the input into the application logic.
4) The code becomes completely inflexible and almost impossible to maintain.
5) The only way to change the face of the application will be to rewrite it.
6) The application logic often contains expertise that you as a GUI programmer don't have, so the real expert (usually not you) has to go wading through your GUI code to tweak it.
7) It's hard to swap in modular behavior. You could end up with a fat-fat client that is a resource hog and memory pig.
Top Rules of a GUI Programmer
You are on your way to becoming a very successful GUI programmer if you follow a few simple rules:
1) Don't mix the application logic code in with the GUI code
2) Don't mix the application logic code in with the GUI code
3) Don't mix the application logic code in with the GUI codeâ€¦
100) Don't mix the application logic code in with the GUI code
Sound redundant? It is, but if this rule is drilled into your head and you follow it, you will be hailed as a hero by all your cohorts in the field. Another way to think of this is to ask yourself the following question:
1) "Does every piece of code tailored to a specific function of expertise live in a different assembly or class?"
What do I mean by this? Next time you write an application and you are mucking around in the GUI, ask yourself the following question, "Before I go ahead and type this line of code, does the code require knowledge outside of the GUI?". If the answer is yes, DON'T TYPE!! Sit back and decide where the code should exist (outside of the GUI). Is there an assembly or class where that expertise belongs? Do I need to create an assembly or class to put that piece of expertise?
Another question to ask yourself is:
2) "Does the line of code I'm about to type inside the GUI have anything to do with presentation or input?"
If the answer to the question is no, DON'T TYPE!! It probably doesn't belong in the GUI.
The only exception to putting non-presentation related code in the GUI is to call a nicely encapsulated API that handles the expertise outside the GUI.
A pattern called Model View Controller was created to help the application developer separate out the presentation layer (GUI) from everything else. The View (GUI) talks through the controller to the Model. The model contains the data and application logic. Unfortunately, the way .NET is designed methods like event handlers which belong in a Controller end up in the View. This is another reason why inexperienced programmers regress into the habit of dumping application logic into the GUI. The environment is encouraging this behavior! Microsoft seems to have intended controller to be "taken care of" through .NET's internal plumbing. The way to tighten the controller concept is to create your own controller that lives in these event handlers and deals with communication from the view to the model through the controller's method calls. The model you are communicating with consist of a separate set of classes, interfaces, legacy dll, database, and/or hardware, that performs all the complex behavior(s) specific to the requirements of your application. The expertise of your application only lives inside the model and is isolated from the GUI (view). To understand more about how model-view-controller can interact with C#, check out Matt's article on MVC.
When you start to design your model and are considering where all your expertise should live, make a clear distinction between the different types of expertise required by the system and create a list of categories to house them. For example, I may have an application that requires logging, configuration management, reading a list of customers from a database, and running some scripts. Each one of these functions requires completely different expertise to implement, and they are all are mutually exclusive. Each function should be controlled by a different assembly or class (none of which live inside a Form class!). You can then define more specific functions that live inside each of these areas of expertise. For example, say you need to be able to log errors and log alerts. You may end up defining a method called LogError and LogAlert or Log(LogType x) or whatever fits your design needs. What you end up with is a black box defining that particular functionality,consisting of inputs and outputs. You don't care what lives inside. Particularly, your GUI doesn't care what lives inside. In fact, your GUI should never have any knowledge of what lives inside. You may be laughing at this point, and saying, "Duh, I know thatâ€”hey, by the way, why not just use the WriteStream class in the middle of my Form to log stuff since it's already part of the .NET framework?". This is where the black box concept often falls apart in the hands of a beginning programmer. Inevitably, down the road the client will say, "Hey, can you print that log to the console, not a file please?" At that point the novice GUI programmer finds himself going through every WriteStream and replacing it with Console.WriteLine. Multiply this sort of code changing a hundred-fold, and you end up with a pissed-off client. This leads me to question number three:
3) "Does the line of code I am about to type in my GUI have any low level framework calls?"
If the answer to this question is yes, DON'T TYPE!! All low level calls should be delegated into specific areas of expertise in the model and should never live inside the GUI (ever).
Keeping it Simple
How can someone tell at a quick glance whether a software design was applied to a C# Windows Form GUI or not? One way to tell is to open the main Form and look at the methods. If there are more than 20 lines of code in most of the methods there is a good chance that the GUI code contains application specific logic. If the code is littered with loops within loops, if-else structures, and switch statements, I guarantee that the application logic is in the GUI. If the form is rigorously using the .NET library and low level classes, it probably contains application logic that doesn't belong there. Which brings me to my final question to ask yourself as a GUI programmer:
4) "Does the lines of code I am about to type in my GUI require a lot of thinking?"
If the answer to this question is yes, DON'T TYPE!! You are probably ready to type in some horrendous structure that the next programmer will spend a good portion of his formative years debugging. All the thinking should be in your well-designed model. The GUI is stupid, mindless, and uncomplicated. Its only purpose in life is to accept input and present output. It should require very little thinking. The moment you find yourself thinking of some logic structure to put inside your GUI, please stop. It belongs in the model, period.
In my entire programming career, half of my responsibility was to clean up messy applications which had no boundaries between the presentation layer and the application logic. If you adhere to some of the rules in this article, a lot of bad code can be avoided. I know I am not alone here, so please feel free to tell your story in the comments at the bottom of the article. As more programmers become aware of the disasters that are easily created and difficult to fix, this knowledge will hopefully lead to better design and happier clients (and less work for me!), In the meantime, if you are new at this game, my best advice is: model your programming so that others will view your efforts with respect and admiration when working with C# and .NET.