Tuesday, July 8, 2008

The Basic Design: MVC

As software developers, we have to make design decisions constantly. Something seemingly as simple as a bug fix can raise questions regarding design. I know that while Will and I analyzed the problems we were confronted with, we always ended up with the same general design guideline: Separate the business logic from the user interface.

I understand that this isn't a ground-breaking discovery, but it's something that all developers I know have learned the hard way. In fact, the Model-View-Controller Pattern (MVC) is the de facto design for the aforementioned solution. Unfortunately, the pattern doesn't spell out the class definitions and their interactions specifically. Instead it leaves those details to the developer. Hence, it's specification as a pattern. Not to mention the slight variations of it's definition a developer will find when typing "MVC" into the Google search box. I'd like to explore this pattern and describe a possible implementation in C#.

Before I do so, I feel it's appropriate to mention that the ASP.NET team has created a MVC Framework, which I will not be discussing in this blog.

First, let's define the problem. We have an application that doesn't have a clear design to it and we've been asked to implement a feature that adds, removes, and updates users and roles. Roles can contain many users as well as many roles. The users and roles will be used throughout the application and we know that maintenance will become an issue if we don't start thinking about the overall design.

The first step is easy: Define a User class and a Role class.



Now that we have the objects defined, we have to understand how interactions with these objects will take place. This is where the user interface comes into play. Several UI objects (windows, dialogs, etc...) need access to the objects.

Since the UI needs to display the users and roles as well as have an interface for adding, removing and updating them, we can certainly create instances of the objects all of the UI classes. If we do this, we soon realize that every time we add a user to a role, we have to call the appropriate methods elsewhere in the UI code to persist the changes. This becomes problematic because every time we add a UI object, we have to call methods on those objects as well as have references to them.

Example code in one of the UI objects:
private void OnAddRoleClick(object sender, EventArgs e)
{
Role newRole = new Role();
m_Roles.Add(newRole);

m_SomeInterface.AddRole(newRole);
m_AnotherInterface.AddRole(newRole);
}
What if a third UI object is created that is interested in roles? That object will also have to maintain a role collection, pass it's instance passed to the two existing UI objects, and expose public method to add have roles added to it. In other words, for every UI object added, every existing UI object must be updated. This isn't going to be easy to manage in the long run.

Now is a good time to separate the business logic from the UI. To do this, we will create one object that manages the user and role objects and pass an instance of it to the UI objects. Lets call that object "Application."



The Application object represents the Model of the MVC pattern. We already have the View of our application defined by the UI, so now we have to connect the two. This is where the Controller comes into play. The Controller is responsible for responding to events and invoking changes to the Model. In C#, this can be accomplished by raising events from the user and role classes and handling UI events to update the Model in the UI objects.

Here is what the Model will look like with the new Application class and the appropriate events exposed:


All we need to do now is pass an instance of the Application class to the UI objects, handle the UserAdded and RoleAdded events in the UI code, call into the Model from the UI when the user adds roles and users, and we're done! Well, not really. With most design solutions comes a number of smaller design problems. I'd like to explore some of those problems in my next post: Object Models and the Observer Pattern.

No comments: