I�m thrilled by how the Model-View-Presenter Pattern (MVP) is helping me on
the "Get-Filters on Wizard, Show-Result" type of report I�m doing on work right
now. So after grasping a little bit more of how MVP works, and believe me, it
can get quite tricky, I decided to write about it.
I�ll illustrate the MVP
pattern through a really simple sample, but one that could be used in lots of
applications: A login UI. I�ll build the UI in the following steps:
-The Unit
test for the UI (Yeah you read it right! I�ll start with the UI unit
test!)
-The Windows Forms UI
-The ASP.Net UI
I�ll start with a solution that looks like this picture:
But where is the UI project, you�ll ask. That�s right, no UI for now. Let�s
write the first unit test. It�ll fill the username, password and try to
login.
So I create a new Unit Test (without wizard) in the UnitTests assembly
with the name of LoginUITest.
I�ll be using mocks for my unit tests. If you
don�t know about mocks I recommend this
article. Mock objects let you simulate a physical object, in the sense that
you can create a much simpler object than the one your application is really
going to use. By using this technique your Unit Tests get much simpler and by
doing so enable you to write more tests, more efficiently. This concept will get
clearer once I show some code.
So I write my unit test the way I imagine a
login form should behave:
1: private ILoginForm view;2: private LoginFormPresenter presenter;3:4: [TestInitialize()]5: public void MyTestInitialize() {6: //Create a new view (the one that simulates a form)7: view = new Mocks.LoginFormMock();8: //Create the presenter that will be responsible for9: //the behavior of the view.10: presenter = new LoginFormPresenter(view);11: //Initialize the presenter.12: presenter.InitializeView(ViewInitialization.FirstTime);13: }14:15: [TestMethod]16: public void TestLoginUI() {17: //values to test against.18: string targetUser = "heynemann";19: string targetPass = "MVProx!";20:21: //Set the view values to the values I want to use.22: view.Username = targetUser;23: view.Password = targetPass;24:25: //Before invoking the event, I make sure the view26: //retained the values I passed in.27: Assert.AreEqual<string>(targetUser, view.Username);28: Assert.AreEqual<string>(targetPass, view.Password);29:30: //This method is used to simulate a button click.31: ((Mocks.LoginFormMock)view).RaiseLoginButtonClickEvent();32:33: //If the Presenter handled the click event34: //the user should be logged by now.35: Assert.AreEqual<bool>(true, view.IsLoggedIn);36: }
Ok, now I have to make this test pass. The next thing to do is creating the
classes and interfaces used by my unit test.
You can see that there�s a
ILoginForm interface. That interface is responsible for my view. All the forms
that represent a Login Form will have to implement this interface, no matter
whether they are Web or Windows Forms, or whatever else you can think of that
can perform a login operation.
So I create this interface in the UI assembly.
Note that this assembly must not have any references to either
System.Windows.Forms or System.Web.UI since it�s UI independent.
Basically
this interface is the one that will hold my login credentials and if the user
has logged in succesfully.
So I add to the interface the following
properties: Username, Password and IsLoggedIn. The guideline for naming boolean
variables is to prefix them with Is, so that�s why I call it IsLoggedIn and not
LoggedIn. The ILoginForm interface will declare a LoginButtonClick event as well
so our presenter can subscribe to that.
The image for the ILoginForm
interface:
The next thing to do is create an implementation of this Interface in Mocks.LoginFormMock. The purpose of this class is to be the simplest implementation possible for the ILoginForm interface.
Ok, now I must build the LoginForm behavior, which basically is initialization and login. So I create the LoginFormPresenter class. One thing to note is that every single presenter is bound to a view. So the default constructor (with no parameters) is private in the case of our presenter. The only public constructor is one that receives an ILoginForm parameter, since this way we can assure that this presenter will be used in the right way.
The LoginFormPresenter will have two operations: InitializeView(View Initialization) and Login. The first method initializes the view and gets as parameter an enumeration indicating whether this is the first time around or not (in ASP.Net scenarios it�s very common for the initialization to be called multiple times, but only in the first some operations should be performed). The second method is private and will get called upon the LoginButtonClick event raisal, and after that perform the login operation.
Ok, so now our unit test compiles. This is already a major advancement, in the direction that we probably have all classes nailed right now. So now we go to the core of the Test-Driven-Development discipline: the unit testing.
I run the unit test we just created and it passes just fine. Now we are close to finishing our sample.
I�ll build the windows and web UI�s with almost no effort.
Let�s start with the windows UI. I create a Windows Forms project in my
solution called Heynemann.LoginSample.Windows.
The UI for the Windows Form is
just like this:
Now on to the MVP stuff. Let�s get to the code file of this form and make
sure it implements ILoginForm.
So a few important stuff, in the windows forms
application the Username and Password properties map directly to the .Text
property of the corresponding textbox, and the btnLogin click event handler just
calls the LoginButtonClick event passing the view as sender and EventArgs.Empty
as parameters. Well, that�s pretty much it. The Windows client is ready, and
working.
Now onto the web application. I create a website called
Heynemann.LoginSample.Web. Then I open the default.aspx web form and build it�s
UI the same as the windows one. Then I do the same process of implementing the
interface as I did on the windows form. And that�s pretty much it. Our web
interface is working the same as the windows one, with VERY SMALL effort.
For
more informations take a look at the full-solution source code included in this
article.
Hope this helped understand how cool is MVP. For a more thorough reading, please read Martin Fowler�s articles on the subject. You can find them in his website at: http://www.martinfowler.com/
You can view the original blog post that originated this article at http://manicprogrammer.com/cs/blogs/heynemann/archive/2006/08/11/22.aspx. Feel free to leave comments here or there and make sure you sign my blog (good stuff on a daily basis :))
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||