Part 5 of “Building World-class Art Tools” from Develop 08
Today’s post has been a particular pleasure: one of those special times when writing changed my mind. At my original talk at Develop, I said “use Model View Controller”, loud and clear. I even made it key message number 8:
In the talk, I was short of time, so I skimmed over many of the details, thinking I’d explain them in this post. But the more I wrote and researched, the more I realised I couldn’t convince myself that MVC is a good thing, let alone convince my readers. I love it when writing does that to me. It’s an unusually powerful experience, changing my own mind without external persuasion or evidence. Anyhow, the more I wrote, the more I realised it’s an outdated design which is no longer fully relevant to rich client applications like game tools. I thought we were using it, but it turns out we’re not really. Having said all that, several of its key ideas are still important: separating model from user interface is the single most important aspect of your tool’s architecture, and using an observer pattern to refresh the UI when the model changes is also important when multiple views are involved
I’m going to cover the basics of MVC, why it’s not relevant any longer, the ideas from it that are still useful, and how to build a good model.
Why separate the UI at all?
The bigger the software project, the more the architecture matters. Poorly architected applications become hard to maintain and extend, and the problems often don’t become apparent until it’s too late. Adding new functionality requires a rippling series of changes throughout the existing code, causing bugs and unintended side-effects on the way. Well-architected applications, in contrast, are a pleasure to extend: new functionality slots in with ease, barely affecting any existing code. Suppose you’re setting out to build a serious content creation tool: something that’s going to see a lot of use, something you’re going to invest heavily in developing, something that’s going to grow big, be extended and maintained over time. How should you architect it?
For me, there are two critical factors which make content creation user interfaces special:
- The underlying data they manipulate is unusually rich and complex.
- Content creators are expert power users who want powerful tools.
A natural consequence is that you inevitably want to have multiple ways, multiple user interfaces in the same application, for interacting with the same underlying data. For example, 3D modelling applications usually have a 3D viewport (in fact, multiple viewports, configured for different viewpoints), a logical scene hierarchy view (tree or graph), a scripting console, UV unwrapping views, material editors … this is what your power users expect.
Separating the UI has always been a common-sense thing to do. Partly, that comes from UNIX culture (where reusing the functionality in a command-line application is favoured), and more recently it’s been pushed by test-first approaches (since the UI is usually hard to test), but mostly, it just seems to be a clean separation that works well in practice. For game tools, the multitude of views onto one data set suddenly makes it critical. You don’t stand a chance of writing all that UI, unless the underlying logic is shared and separated away.
Model, View, Controller
“Model View Controller” is mature and well-known – almost 30 years old now – but there are surprisingly few good explanations kicking around. Martin Fowler was spot on when he said this:
Different people reading about MVC in different places take different ideas from it and describe these as ‘MVC’. If this doesn’t cause enough confusion you then get the effect of misunderstandings of MVC that develop through a system of Chinese whispers.
My personal understanding of the basic concept has always been along these lines:
- The ‘model’ encapsulates the data the user is manipulating: in our case, the content they’re creating. In a 3D modelling application, it’s probably a 3D scene containing models and meshes. In Photoshop, it’s a layered image. As you’d expect in an object-oriented pattern, the model is not just data, but a set of operations: the valid manipulations on the content. Many articles on MVC call these the “business rules”. I hate that term! Hello, there are programmers in the world who are not working on “enterprise applications”!
- The ‘view’ is responsible for presenting the model visually to the user.
- The ‘controller’ is responsible for taking input from the user and updating the model and/or view as appropriate.
One thing’s immediately clear: it definitely separates UI (V and C) from underlying data (M). That part I like.
Mostly Very Confusing
The View/Controller distinction is something I find deeply confusing. I don’t think I’m alone, either. I mean, the web browser as controller? I don’t think so. It’s an analogy to MVC’s ideas, to be sure, but it’s not actually MVC. Bad explanations are everywhere, so while researching this post, I’ve tried hard to find some good references on MVC. I was pleasantly surprised with a couple of articles, but am probably none the wiser overall; some of them seem to contradict one another somewhat.
To start off with, I found this great page by the creator of MVC, which includes his original report on the subject. Originally, it was called “Thing-Model-View-Editor” … not quite so catchy, huh? Here’s how view and controller are described:
- While the view does represent the model visually, “It may also update the model by sending appropriate messages”. This clearly implies that user input can be routed through the view, even if the view doesn’t handle it directly.
- On the subject of the controller, it does seem to convert raw user input (keyboard and mouse) into higher-level messages sent to the view, but it also provides information to the user “by arranging for relevant views to present themselves in appropriate places on the screen” and “presenting the user with menus or other means of giving commands and data”. This last part is rather more than just processing user input, and isn’t exactly explained with great clarity.
Next, I found this fabulous article by Martin Fowler. It’s a broad survey of GUI architectures, surveying many of the major designs and patterns, and using historical context to make sense of their evolution. If you’re interested in how to architect your UI, you have to read this.
Interestingly, Martin Fowler managed to find a version of Smalltalk-80 from the early 80s, to get to the root of what MVC really means. His description of “controller” is much more of a pure user input thing: “there’s not just one view and controller, you have a view-controller pair for each element of the screen, each of the controls and the screen as a whole.” That fitted with my prior understanding of what the controller does, and I don’t see any mention of controllers “arranging for views to present themselves on the screen”.
If two of the best articles around, by people who’ve seen the original source code (no Chinese whispers involved), can be as confusingly different as this, there doesn’t seem much hope of finding the truth!
No longer relevant
Martin Fowler also points out that MVC originated very early in the history of GUIs, before reusable controls even, and when reusable controls did come along, they packaged up the controller and view functionality together!! He goes on to observe that over time, “one could almost say that MVC disappeared, if you consider the view/controller separation to be an essential part of MVC – which the name does imply.”
That was my thought entirely when I started writing this post. I feel uncomfortable with a V/C distinction if that doesn’t map directly to your code. Suppose you’re using a standard UI toolkit, with standard components such as a checkbox or tree view. Clearly, they’re part view (they present data to the user) and part controller (they handle inputs). So View and Controller don’t map to separate classes in your source code in typical modern UI programming. The truth is that these toolkits just don’t use MVC. True MVC implies separating out these elements.
I think that’s partly why you see so many articles appearing to twist the original idea: they’re trying to use the same names (View and Controller) for different concepts, that do fit in with modern UI toolkits. They’re trying to shoehorn it where it doesn’t fit. They’d really be better off picking new names for their ideas: that’s what Model-View-Presenter does, for example, and it’s all the better for it.
So there you have it. Trying to distinguish View from Controller seems pointless to me. Even if you think you’re doing it, you’re probably doing something different than the original meanings of the words, because modern UI toolkits tend not to support this design. Feel free to forget about it!
So what should you do?
Clearly, you want to structure your UI code cleanly. There isn’t going to be one universal answer to this problem, so you’re going to have to do some work. Here are my top tips, though:
- Did I mention already that you have to read Martin Fowler’s GUI architectures article? Make yourself familiar with all the possible alternative approaches.
- Check out the Eclipse project. I wouldn’t recommend just copying stuff from there, but they have written some good articles about how it all works, and you might get some nice ideas. In particular, look at the Graphical Editing Framework, and its Edit Parts (and yes, I realise these are explained as being MVC!)
- Don’t fight your UI toolkit. Use patterns that seem natural in that context, or you’re causing yourself unnecessary pain.
- Use a design that suits your particular application. Every app is different.
- Whatever you do, make sure the UI is separated from the model.
- If you have multiple views on the same data, use a loosely coupled observer pattern whereby the UI listens for changes to the model. This is important with multiple views, because when one view changes the model, the other views need to refresh themselves. By using an observer pattern, the model doesn’t need to be coupled to the UI. By watching the model, the views are independent of one another, and you can add new views without changing the rest of your UI code. Interestingly, this observer mechanism was one of the key innovations of the original MVC!
Creating a good model
Let’s turn away from the UI side of things, to the model. There are some subtleties to building a good model, and if you get the model just right, it’ll make writing the UI so much easier. As I just mentioned, having an observer mechanism built into the model will allow you to create multiple views on your data without them affecting one another.
One of the biggest mistakes we’ve made in the past is to think we had a model when we didn’t. Here’s the scenario: a library gets written, intended for use in both your editor and your game engine. This library can load and save a particular type of content, and has a bunch of data structures for representing that content in memory. Keeping the data structures, the loading and the saving all together in one place makes plenty of sense. The problem is that this library does not form a model for your editor. It’s part of the model, to be sure, but it’s not enough. Most likely, it doesn’t provide all the manipulations you need to perform on the content while editing it, and more seriously, it’s very unlikely to provide an observer mechanism for informing listeners of changes to the data structures: as I mentioned above, this is a key responsibility of a good model. Sure, you could add these things to your library, but you’re embedding it in the game engine, and these extra things are just going to look like bloat to a game runtime 😦
The solution we use now is to introduce a wrapper layer. Conceptually, this looks like a model to the UI code above, but it doesn’t contain any complex data structures: it re-uses the underlying library for implementation. Where possible, it just forwards commands from the UI to the lower level, which contains the true implementation. If the implementation layer is missing some of the data manipulations you need, you can add them to this wrapper.
Don’t get turned off by the extra layering in the system just yet. Let’s see what cool editor functionality we can hang off this wrapper layer:
- When commands come into the wrapper layer, it can emit notifications of model changes to interested observers (aka the UI code), as discussed above.
- For each incoming command, we can stick a command object on an undo/redo stack, ready to undo later. Done this way, the user interface code doesn’t have to worry about undo/redo. It doesn’t even have to use or care about the sometimes-slightly-unnatural command pattern: it gets to use a normal API. If you like using data binding in your UI framework (you should: it’s “programming without moving parts”), you can: your UI binds to the wrapper, not the underlying data. Ordinarily, data binding tends not to support undo/redo. In this system, the UI can’t really break and bypass the undo/redo, because everything has to go through the wrapper layer.
- The wrapper can keep a record of all the calls passing through it. How many times have your artists reported a bug, only to find they can’t reproduce it? Your testers then spend ages trying, sometimes resorting to sitting and watching the artists at work, waiting for it to happen again. With a record of everything the artists are doing, your chances of reproducing bugs are much improved. The simplest possible implementation is to write a line to a log file for each call; this will provide useful diagnostic information to your developers. The gold-standard implementation is to write the information out in a form that can be loaded and replayed later. This could be anything from some kind of binary serialisation, to writing the calls out in the form of a Python script.
At this point, I hope you can see that the wrapper isn’t just another layer for the sake of it. It provides a set of critical functionality to your editor, while enabling the implementation library to focus on its core functionality, and allowing the UI code to interact with the model via an extremely natural API, including data binding. Nice! If you’re worried that the wrapper would appear to contain too much boilerplate code, you can probably find ways to autogenerate much of it 😉
This system is so powerful, that we’d definitely apply this pattern even if we were building a new model from scratch. What started out as a simple way to re-use some code that hadn’t been designed with editors in mind has turned into a best practice for building the model in our content creation tools 🙂