Patterns for everyone
Some 40 years ago Trygve M. H. Reenskaug and some people at Xerox came up with this new thing we call Model-View-Controller. These days architectural design patterns would make for some rather cruel quizzes and since I like cruel let's have one! I just made up one of the following acronyms. Which one?
- MVA
- MVC
- MVP
- MVVM
- POMV
- SMVM
- TMVE
Of course that'd be the Plain Old Model-View (POMV) which I just made up to represent the traditional way of not having anything sitting between the Model and the View. Coming up with yet another MV-Pattern was not the point of this article though. I'm more interested in the existing ones and what problems they are trying to solve. Also it is probably worth pointing out that this is not meant as an exhaustive study of the patterns so if you are looking for that, go ask the Google!
The underlying problem
Let's approach the problem of software design without any patterns first. It is pretty common in software development that the managment comes up with a need to view and process some data. This data might be located in their own database or it could be provided by a third party through a web service or something. What matters is that it is somehow predefined in most cases and it needs to be accessed - the first solution would of course be to teach the management some SQL or HTTP but there is an issue here. The people in management are evolved from apes and might have difficulties cross joining dozen tables or parsing HTML in their head. Even if they manage to learn some SQL, they'll soon start requesting some sort of easier user interface. They'll even be extra helpful and provide some simple interface sketches!
This is all the managers care about. They have some data and they want an interface to that. Programmers should figure out how to do that - and why not? It's easy! Let's take an example of an application which is used to input new people to a database because those INSERT INTO statements are just evil. Below is a part of an application in C# that could be used for this purpose.
Listing 1: Person creator using POMV-pattern
public partial class MyForm : Form {
public MyForm() {
// Initialize the graphical layout designed with the
// Forms designer and wire the event handlers.
InitializeComponent();
// Populate UI controls from the model.
DBAO model = DBAO.GetInstance();
CompanyDropDown.ItemSource = model.Companies;
}
public HasCompanyCheckBox_CheckedChanged(object sender, EventArgs e) {
// Modify the UI based on check box value.
CompanyDropDown.Visible = HasCompanyCheckBox.Checked;
}
public SaveButton_Clicked(object sender, EventArgs e) {
// Create a new person when the user clicks the save button.
string name = NameTextBox.Text;
Person person = new Person(name);
if (HasCompanyCheckBox.Checked)
person.Company = CompanyDropDown.SelectedItem;
DBAO model = DBAO.GetInstance();
model.Save(person);
}
}
And I guess using a separate data access object instead of doing string concatenation for unparamerized SQL in the event handlers is pretty advanced in this case but I want to concentrate on the View here.
That should do the trick and the management will be happy for another week; That's when they come up with these three new features they want to have. And in a year the functionality of the one window will be built out of some thousands of code lines - in one file.
At some point the developers get frustrated and decide to move parts of the logic into another file at which point they face the question: Which lines should they move? This is the question the different architectural patterns try to answer - in different ways.
At this point the MVC is the simplest out of these. The very basic idea is to move all the non-UI things out of the view. Reading things from the Model (lines 9-10) can stay since they act upon the UI but writing to database (lines 27-28) should go as they don't really have anything to do with the UI other than taking some values as user input.
Listing 2: Person creator using MVC-pattern
public partial class MyForm : Form {
public MyForm() {
InitializeComponent();
// Populate UI controls from the model.
DBAO model = DBAO.GetInstance();
CompanyDropdown.ItemSource = model.Companies;
}
public HasCompanyCheckBox_CheckedChanged(object sender, EventArgs e);
public string PersonName { get { return NameTextBox.Text; } }
public bool HasCompany { get { return HasCompanyCheckBox.Checked; } }
public Company ParentCompany { get { return CompanyDropDown.SelectedValue; } }
public event EventHandler SavePerson;
}
public class MyController {
public MyController() {
myView = new MyForm();
myView.SavePerson += new EventHandler(View_SavePerson);
}
public View_SavePerson(object sender, EventArgs e) {
// Create a new person when the user clicks the save button.
string name = myView.PersonName;
Person person = new Person(name);
if (myView.HasCompany)
person.Company = myView.ParentCompany;
DBAO model = DBAO.GetInstance();
model.Save(person);
}
}
This is a rather easy solution to the original problem. The development team has now one piece of code that deals with the UI logic and anything that isn't directly related to modifying the UI will be moved to another file. Everything goes fine until someone decides that the Database needs some refactoring. This means that everything that uses the Model is suspectible to changes. The UI logic is sitll pulling information from the Model and needs to be maintained when the Model changes. The new Controller layer has large amount of functionality related to the Model and also needs to be maintained. So since the Development team is doing some refactoring they decide to solve this new problem in such a way that in future they don't need to deal with both the View and the Controller when the Model changes.
And here they need to choose between the rest of the patterns: MVP variant Passive View, MVA or MVVM. The basic idea behind these all is the same: Remove logic from the View so it's simple; Move logic to the thing between the Model and the View. The main difference is the way this is implemented which I'm not going to analyze as I'm not completely sure about all the different details myself. I'll just provide a Passive View/MVA variant of the example program.
Listing 3: Person creator using MVP/MVA -pattern
public partial class MyForm : Form {
public MyForm() {
InitializeComponent();
}
public HasCompanyCheckBox_CheckedChanged(object sender, EventArgs e) {
// Modify the UI based on check box value.
CompanyDropDown.Visible = HasCompanyCheckBox.Checked;
}
public string PersonName { get { return NameTextBox.Text; } }
public bool HasCompany { get { return HasCompanyCheckBox.Checked; } }
public Company ParentCompany { get { return CompanyDropDown.SelectedValue; } }
public event EventHandler SavePerson;
public CompanyCollection Companies {
get { return CompanyDropDown.ItemSource; }
set { CompanyDropDown.ItemSource = value; }
}
}
public class MyAdapter {
MyForm myView;
public MyAdapter(MyForm view) {
myView = new MyForm();
DBAO model = DBAO.GetInstance();
myView.Companies = model.Companies;
myView.SavePerson += new EventHandler(View_SavePerson);
}
public View_SavePerson(object sender, EventArgs e);
}
In this version the View doesn't have any logic which is tied to the Model. The Adapter is doing all the lifting and just tells the View what to do. The only programmed logic in the View is at lines 7-10 and is dealing with the UI.
Conclusions
These samples are rather minimalistic and I'm not even mentioning the effects on testability or other things here. But I believe these samples are a sufficient for describing the differences between the patterns. I would say that the Passive View/MVA variant has the cleanest separation and the POMV variant we began with has the worst. The real question is which one would you use if you were to write this application? I'm pretty certain I'd be going with the POMV one in the start. In the current form, what are the benefits of the MVC or Passive View versions?
I'm not sure cleaner separation of responsibilities is enough to offset the fact that the unabbreviated versions of the two latter variants contain 50% more code and twice the amount of code files than the first one. The point is that complex design patterns add complexity to the code and their benefits are usually noticeable only on larger programs. I have experienced this the hard way with the previous versions of this site (Picture 1). The first iterations failed because my coding style in PHP didn't provide enough control over complexity and the next iterations failed because the architecture of JavaEE resulted in too much overhead which turned the simple site into a complex problem.
Picture 1: Program complexity in terms of architectural pattern and original complexity.
So in the end the quest for the best pattern continues. We are pretty certain we want to have the database oriented stuff at a layer different than the one where we have the UI logic. But no one has yet come up with the universal solution on what we should use to glue these two layers together.
I know I didn't explain the MVVM pattern. I skipped it for now because I'm pretty certain I will be writing something about it soon enough and in more depth as I currently consider it one of the best patterns. But this is my strong opinion.