MVC. Model View Controller. Ahhh. One of those pattern things that your java kids are fond of. And now getting a lot of love from the RoR crowd.
From what I can tell so far, one of the main historical uses of MVC is to give dull people something to argue about.
"No no -- that's not pure MVC! If you go back to page 97 of GoF..."
And yet... I'm intrigued. The promise is nice.
I haven't found any simple examples using MVC for a .net application. So I'll give it a go. And it will be bad and you will tell me how to improve it ;-)
It's the view/controller bit that I'm a little mixed up with.
First, here's my architecture (notice the small 'a' in architecture... )
View
a windows form in this example
/\
||
\/
Controller
class that co-ordinates
between Model and View
/\
||
\/
Model
Your business object, an
'AppUser' in this example
Have I gone off the track yet?
Principles
- No 'business rules' in the view.
- The view in fact knows nothing about the model.
- The 'model' knows nothing about the view.
- The less code in the view, the better.
- The controller doesn't care whether the view is a web form or a windows form.
Before I show you this approach -- let me point out that i'm not advocating it -- i'm just throwing it out there, to see what problems you've got with it.
Okay so inside our Login form, we have this variable:
Private WithEvents m_LoginController As New LoginController
So, the Login Form has a member variable of type LoginController
and as it happens we've declared that we're interested in events that this object might raise (i.e. 'WithEvents
') (we'll see the reasons for this soon enough).
(Question: Should we have one controller for each form, like i'm doing here? or one controller for the whole app? If we have one controller for each form, then our view platform is dictacting the structure of our control -- since the type of viewing platform will determine the optimal complexity of a single form. Never mind... Let's make it one controller per form and see if anyone complains.)
Okay when the Login button is clicked we tell the controller.
Private Sub btnLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLogin.Click
'The view doesn't know what a user is!
'The view doesn't know how passwords are validated...
'It just **looks pretty** and **does what it's told!**
m_LoginController.Login(txtUserName.Text, txtPassword.Text)
End Sub
The login controller is notified.
And that's all the code we have in any event handlers from our controls. Seem easy.
Now what happens inside this Login Controller?
Okay -- so this login controller is going to have to talk to "the model". I figure the model contains my business objects: in this case there's probably a "User" class of some sort. Let's say that there's a class called AppUser
and it's in a namespace called Model
.
Public Class LoginController
'The controller knows what's happening on the view... but doesn't know the
'names of the controls... or anything about the way the thing looks.
Public Sub Login(ByVal Username As String, ByVal Password As String)
'The controller knows a bit about the back-end.. the 'model'
Dim currentUser As New Model.AppUser
Dim result As Boolean = currentUser.Login(Username, Password)
'!! Somehow notify the View!!
End Sub
End Class
The Login controller also has to talk back to the view somehow: and for this rambling demo I've chosen to use "events". We can notify the view that something has happened...
Public Class LoginController
Public Event LoginFailure(ByVal Message As String)
Public Event LoginSuccess(ByVal Name As String)
Public Sub Login(ByVal Username As String, ByVal Password As String)
Dim currentUser As New Model.AppUser
Dim result As Boolean = currentUser.Login(Username, Password)
If Not result Then
'!! Use events to communicate with the view.
RaiseEvent LoginFailure("Login failed")
Else
RaiseEvent LoginSuccess(currentUser.Name)
End If
End Sub
End Class
Now back in the windows form, we have handlers for the events raised by the Controller.
Private Sub ShowLoginFailure(ByVal Message As String) _
Handles m_LoginController.LoginFailure
'Clear the password field.
txtPassword.Text = ""
'Display the failure message
lblMessage.Text = Message
'Set focus to the username control
txtUserName.Focus()
End Sub
Private Sub ShowLoginSuccess(ByVal Name As String) _
Handles m_LoginController.LoginSuccess
'Notice that the view has decided not to do anything with the 'Name' parameter.
'The view gets to make all the visual decisions
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub
Now I've got a bunch of problems with the above implementation. It seems over-engineered, adds extra code for little benefit.
Am i doing it wrong? Thinking about it wrong? Or both?
What do you see?