Model View Controller: History, theory and usage![]() Recently I have been fascinated with how to structure web-applications, especially web-applications that use a lot of JavaScript. To learn more I have done some research into one of the most widely used design-patterns: Model-View-Controller (MVC). This blog post explores how MVC is used in Smalltalk, Ruby On Rails, .Net and Cocoa. I also implement Smalltalk's and Cocoa's MVC patterns using jQuery and CoffeeScript. The history of MVCMVC is really old and was first described in 1979 by Trygve Reenskaug, then working on Smalltalk at Xerox PARC. Being over 30 years ago does not mean it's dead, far from it... Today MVC is used in most modern web and GUI frameworks, including:
The main objectives of MVCModel-View-Controller as the name applies considers three roles:
There isn't a standard definition of the MVC pattern and many frameworks use a slightly different version. This said there is a core idea of what MVC tries to achieve:
In most implementations the separation between the model and the view is most important. Martin Fowler's notes following interesting thing about Smalltalk's original MVC pattern [in his book Patterns of Enterprise Application Architecture]:
To make things more clear let's take a look at how the MVC pattern is implemented across different programming languages and frameworks. Smalltalk MVCThe first implementation of the MVC pattern was found in Smalltalk-80 - a programming language released in 1980. Smalltalk is a pure object oriented programming language that's very interactive and MVC was one of the core features of the implementation. The main objective of using MVC in Smalltalk was separating the application logic from the user interface - - enabling reuse of the model to implement several user interfaces:
Smalltalk's dependency between model, view and controller:
Things to note about Smalltalk's dependency structure:
Ruby On Rails MVCRuby on Rails is a very popular Ruby web-framework that uses MVC. The client-server environment of web-applications makes things more complicated than the original environment that MVC was invented in. This is mostly because the view can't easily observe the model (at least not without Ajax or Comet communication). Overview of Rails MVC:
Things to note about Rails MVC:
Cocoa MVCApple's Cocoa framework is used to create Mac OS X and iOS applications. Cocoa uses a lot of design patterns and MVC is one them. Cocoa's MVC puts a lot importance on the controller and this makes it a lot different than the original Smalltalk MVC. A Cocoa controller acts as a mediator between the view and the model [this is commonly known from the Mediator pattern]:
The reason to use the mediator pattern is probably because the data flow is cleaner than the traditional MVC found in Smalltalk, which looks like this:
The above graphics are from Apple's Cocoa Design Patterns. I recommend reading these documents if you want to know more about Cocoa's design. ASP.Net MVC frameworkASP.Net started off using a pattern called Model-View-Presenter, but this pattern got "replaced" when Microsoft introduced ASP.Net MVC, which implements a similar MVC pattern used in Rails or Django. CodeGuru has a good blog post on MVP vs. MVC and in it there's a good graphic explaining the difference between these two patterns:
Exploring MVC with jQuery and CoffeeScriptI have done some experiments with sample implementation of MVC patterns using jQuery and CoffeeScript. First do note that the idea of using MVC in JavaScript isn't new and there are already some JavaScript frameworks that use this approach: In following implementations I will only implement Smalltalk's and Cocoa's MVC patterns. Rails or ASP.Net MVC patterns are done in a backend environment and I don't think it makes sense to use them for client-side code. What example to implement?To make things as simple as possible we want to implement a counter button that increases its value when clicked. A simple HTML implementation could look like this: <button onclick="this.innerHTML = parseInt(this.innerHTML) + 1">1</button>
We are interested to see how the above code could be structured using a MVC pattern. Following examples will use a lot more code than a simple HTML implementation and this is fully expected. The reason to use MVC isn't to use fewer lines of code, but to structure code so it's easier to reuse, maintain and test. Implementing Smalltalk's MVCSmalltalk's MVC did not make a separation between the view and the controller and it used the observer pattern to update the view. Following this philosophy the button example could look like this: class ModelCounter
constructor: ->
@observers = []
@value = 1
increaseValue: (delta) =>
@value += delta
@notifyObservers()
notifyObservers: =>
obj.notify(this) for obj in @observers
registerObserver: (observer) =>
@observers.push(observer)
class ViewCounterButton
constructor: (opts) ->
@model_counter = opts.model_counter
@button_class = opts.button_class or 'button_counter'
@model_counter.registerObserver(this)
render: =>
elm = $("<button class=\"#{@button_class}\">
#{@model_counter.value}</button>")
elm.click =>
@model_counter.increaseValue(1)
return elm
notify: =>
$("button.#{@button_class}").replaceWith(=> @render())
The above code could be initialized with following code: $(document).ready( ->
model_counter = new ModelCounter()
view_counter = new ViewCounterButton('model_counter': model_counter)
$('body').append(view_counter.render(),
view_counter.render())
)
Implementing Cocoa's MVCLike noted before Cocoa's MVC pattern uses the Mediator pattern so the controller acts as a mediator between the view and the model. Following this philosophy our button example could look like this: class ModelCounter
constructor: (@value=1) ->
increaseValue: (delta) =>
@value += delta
class ControllerCounter
constructor: (opts) ->
@model_counter = opts.model_counter
@observers = []
getValue: => @model_counter.value
increaseValue: (delta) =>
@model_counter.increaseValue(delta)
@notifyObservers()
notifyObservers: =>
obj.notify(this) for obj in @observers
registerObserver: (observer) =>
@observers.push(observer)
class ViewCounterButton
constructor: (opts) ->
@controller_counter = opts.controller_counter
@button_class = opts.button_class or 'button_counter'
@controller_counter.registerObserver(this)
render: =>
elm = $("<button class=\"#{@button_class}\">
#{@controller_counter.getValue()}</button>")
elm.click =>
@controller_counter.increaseValue(1)
return elm
notify: =>
$("button.#{@button_class}").replaceWith(=> @render())
The above code could be initialized with following code: $(document).ready( ->
model_counter = new ModelCounter()
controller_counter = new ControllerCounter('model_counter': model_counter)
view_counter = new ViewCounterButton('controller_counter': controller_counter)
$('body').append(view_counter.render(),
view_counter.render())
)
Conclusion and further readingModel-View-Controller has lasted over 30 years and is used in most modern GUI-frameworks and web-frameworks. It has proven its worth and it's here to stay. I would expect it being used more and more, especially for next-generation JavaScript frameworks. As my examples show, MVC does produce more code than hack-and-slash HTML code and it's not that suitable if you code small web-applications. But it should be very suitable if you want to code large web-applications where maintainability, reuseability and testability are important. I suggest reading following books and posts if you want to dig deeper into the MVC pattern: Book references
Article references
22. Mar 2011
•
Code
·
Code improvement
·
Design
·
JavaScript
|
|