I find whenever I work with GUI code, the code tends to bloat faster then other kinds of code. It also seems harder to refactor. Whereas in other kinds of code I can refactor pretty easily -- I find I can decompose a larger class into smaller pieces of functionality -- with most GUI frameworks I'm often bound to a framework that requires my widget/control/whatever class to implement a lot more things more directly in the widget/control/whatever. Sometimes this is due to needing to either (a) inherit off of some base widget/control/thing or (b) need access to protected methods.

I typically also have to, for example, respond to a large variety of inputs via signals/events/whatever from the framework to implement all the modes of interacting with the user. I might need in one GUI widget/control to handle a large variety of input/output that might include:

a right click/context menu

reacting to selections from the context menu -- which may be many

a special way to paint the GUI

react to keyboard input

buttons, checkboxes,

etc etc

... all the while manage the classes under the GUI representing business logic.

A simple straight-forward GUI can have its code grow pretty quickly, even when seperating out buisiness logic and using MVC, I find GUI code is a big magnet for change.

Is there any way to manage GUI code in a sane way and avoid letting it become a broken window? Or is a mass of random event handlers/overridden methods really the best we can do for GUI code?

10 Answers
10

The thing to remember about GUI code is that it is event-driven, and event-driven code is always going to have the appearance of a mass of randomly organized event handlers. Where it gets really messy is when you try to shoehorn non-event-driven code into the class. Sure, it has the appearance of providing support for the event handlers and you can keep your event handlers nice and small, but all of that extra support code floating around makes your GUI source seem bloated and messy.

So what can you do about this, and how can you make things easier to refactor? Well, I would first change my definition of refactoring from something I do on occasion to something I do continuously as I code. Why? Because you want refactoring to enable you to more easily modify your code, and not the other way around. I'm not simply asking you to change the semantics here, but asking you instead to do a little mental calisthenics in order to see your code differently.

The three refactoring techniques that I find I use most commonly are Rename, Extract Method, and Extract Class. If I never learned a single other refactoring, those three would still enable me to keep my code clean and well structured, and from the content of your question, it sounds to me like you will probably find yourself using the same three refactorings almost constantly in order to keep your GUI code thin and clean.

You can have the best possible separation of GUI and Business logic in the world, and still the GUI code can look like a code mine has been detonated in the middle of it. My advice is that it doesn't hurt to have an extra class or two to help you to manage your GUI properly, and this does not necessarily have to be your View classes if you are applying the MVC pattern - although frequently you'll find the intermediary classes are so similar to your view that you'll often feel an urge to merge them for convenience. My take on this is that it doesn't really hurt to add an additional GUI-specific layer to manage all of the visual logic, however you probably want to weigh up the benefits and costs of doing so.

My advice therefore is:

Do nothing directly behind your GUI except to invoke and define how the GUI will hook into the View (or an intermediary layer).

Don't try to shoe-horn every view related thing into a single class - or even a single class per GUI window - unless it makes sense for you to do so. Your alternative is to create lots of little and easy to manage classes to manage your GUI logic.

When your methods are starting to look a little larger than a 4-5 lines of code, examine if this is necessary and if it is possible to extract a method or two so that you can keep your methods lean, even if this means a class with many more methods.

If your classes are starting to look really large, start by removing ALL of the duplicated functionality, and then see if you can logically group your methods such that you can extract another class or two.

Think about refactoring every time you write a line of code. If you get a line of code to work, see if you can refactor it to avoid duplicating functionality, or to make it a little leaner without changing the behaviour.

Accept the inevitable, that you will always feel that one part or another in your system will start to feel a little bloated, especially if you neglect refactoring as you go. Even with a well-factored code base, you can still feel as if there is more that you could do. This is the reality of writing software, that you will find yourself always feeling that something more could have been done "better", so you need to strike a balance between doing a professional job, and gold-plating.

Accept that the cleaner you try and keep your code, the less bloated your code will seem.

I think many of the problems you are experiencing can be traced back to a simple cause. Most developers do not treat GUI code like 'real' code. I have no evidence or statistics here, just my gut feeling.

Maybe they think it is 'just presentation' and not important. 'There is no business logic there', they say, 'why unit test it'? They laugh when you mention object orientation and writing clean code. They do not even TRY to make things better. There is no structure to start with, they just slap some code and let it rot as others add their own touch over time. A beautiful mess, graffiti code.

GUI code has its unique challenges therefore has to be treated differently and with respect. It needs love and developers who want to write it. The ones that will keep it thin and give it good structure and correct patterns.

+1 for alluding to the perception of GUI code being treated differently to non-gui code. I've lost count of the number of times I heard someone say, "don't bother to test the GUI because it's not cost effective and besides it's so difficult to do". I usually translate to "It's difficult and I'm too lazy to learn how to do it!".
–
S.RobinsApr 17 '12 at 10:44

1

+1 Where I work we often don't review GUI code - "it's just GUI, skip it". And I'm as guilty as anyone. Strange thing is that in my personal projects I spend a lot if time trying to get nice clean GUI code. Guess it's just a culture thing.
–
HappyCatApr 19 '12 at 17:52

It is easy to abstract the ideas to other frameworks and languages. The main benefit of MVP (in my opinion) is unit-testability. And you only get that, if your code that is not bloated and not spaghetti (judging by your question, this is what you want). It works by introducing a view logic layer called the presenter. The actual view is decoupled from this via an interface (and thus can easily be mocked in unit tests). Now, since your view logic layer (the presenter) is freed from the internals of the concrete GUI framework, you can organize it like regular code and you are not tied to e.g. Swings inheritance hierarchy. Ideally you could switch GUI implementations in different frameworks as long as they conform to the same interface.

For some reason GUI code creates a blind spot in developers about separation of concerns. Maybe it's because all the tutorials lump everything into one class. Maybe it's because the physical representation makes things seem more closely coupled than they are. Maybe it's because classes get built up slowly so people don't recognize they need refactoring, like the proverbial frog being boiled by slowly turning up the heat.

Whatever the reason, the solution is to make your classes a lot smaller. I do this by continually asking myself if it's possible to put what I'm typing into a separate class. If it's possible to put into another class, and I can think of a reasonable and simple name for that class, then I do it.

Structure means paying a lot of attention to using the least amount of code and the maximum amount of frameworks, libraries etc.

Simplicity means keeping things simple from initial design to actual implementation. Keeping the navigation simple, using simple plugins, keeping the layout fairly 'plain' will all help here. They can now be 'sold' to clients/users that can quickly see the advantages of pages that work on pc's, ipad, mobile and other devices.

Testing means including browser-testing tools (webrat and capybara come to mind with my rails work) that catch cross-browser issues up front when better code can be designed to deal with them at the beginning as opposed to the frequent 'patching' of code by different developers as they are 'discovered' by users of different browsers.

Syntax.
It's really helpful to use a code checker / IDE / editor-plugin, etc. for your HTML, CSS, Javascript, etc. The advantage that browsers have obtained through being able handle ill-formed HTML works against you when different browsers perform differently with it, so a tool that checks your HTML format is essential. Having well-formed HTML is very helpul in having unbloated HTML as bad code should have more visibility.

The solution I've found is declarative code. Using just procedural code is a recipe for spaghetti GUI code. Sure, a "special way to paint the widget" is probably going to remain code. But this is code isolated in a class. Event handlers, keyboard shortcuts, window sizes - all that messy stuff is best declared.

Applications such as Word Processing, Graphics editors, etc. have complex interfaces and their code can't be simple. However, for business applications, the GUI does not have to be so complex but some how it still is.

Some of the keys to simplifying GUI are (most apply to .NET):

Strive for simpler design whenever possible. Avoid fancy behaviors if not called for by the business.

Use a good controls provider.

Don't create custom control functionality in the client code itself. Instead, create user controls that extends the original control in such a way that you can reflect your specific behaviors in the controls rather than in the code of the using form/page.

Use a framework (even a home grown) to handle internationalization, resources management, styles, etc. so that you don't repeat this code in every UI.

One thing that has helped me simplify the GUI code is to make sure that the GUI has its own data model.

To take a simple example, if I have a GUI with 4 text entry fields, then I have a separate data class that keeps the contents of those 4 text entry fields. More complicated GUIs require more data classes.

I design a GUI as a model - view. The GUI model is controlled by the application controller of the application model - view - controller. The application view is the GUI model, rather than the GUI code itself.

Separate presentation and model
Use a MV-whatever library/framework, or write your own, to help separate view/controller logic from data model.
All communication with the backend should be done inside the model, and the model state should always be in-sync with the backend.

Decoupling
If object A knows about object B, then A can call methods on B, but B should not know about A. Instead A can listen to events from B. It makes sure there is no circular dependency.
If your app has lots of events between components, then create an EventBus, or leverage an event-driven framework like Twitter Flight.

Partial vs full render If your view is a table or list of items, your may be tempted to create methods like "add", "remove" to insert/delete one item into/from the collection. Your code could easily bloat when you have to support sorting and pagination. So my advice is: simply re-render the whole view even when there is a partial change. What about performance? well if your collection is big, then your should do pagination anyway. Web developer: make sure your event handlers are delegated to the view's root element which does not change.

View model When your view's state becomes too complicated to maintain, for example, a Table view has to keep track of the row data, column data, sort order, currently checked rows (if it supports multi-check), etc, you should probably create a ViewModel object for those states. Your View object should call setters on the ViewModel if something changes on the UI (eg: user checks a row); and it should respond to ViewModel's change event by updating the UI. Usually you should avoid updating the UI if the change event is triggers by UI.

You want to take a look at the concept of "databinding". This is a way connect UI elements to abstract model elements in a declarative way such that the model elements are automatically synchronized with the contents of the UI. There are many benefits of this approach, for example not having to write event handlers yourself to synchronize data.

There is databinding support for many UI frameworks, for example .NET and Eclipse/JFace.