I’ve been working on a RSS reader Windows Phone 7 application as my 2nd Continuous Client example. I’m not diving into the Continuous Client work in this post so you won’t see any of that included (I will at a later time) but I decided to take this opportunity to improve on my MVVM (Model-View-ViewModel), separation of concerns and writing more maintainable and testable code.
I had a great opportunity to pair program with Glenn Block and we went through several iterations of refactoring. I’m amazed at his ability to see small parts of a code base and within a couple minutes he has a strategy for decoupling and separating the concerns.
This series of blog posts shouldn’t only be for people interested in MVVM. In general I learned a lot of of values that I’ll take with me in different problem sets and I hope I am successful in translating those into this series.
One thing I’ve struggled with for a long time is how to cut the dependency on the IoC container. I knew that Service Locator code was bad but every time I tried to reduce the coupling I would run into road blocks where I had to expose the container.
We started at the ViewModelFactory<TViewModel, TDesignViewModel> and worked our way out from there.
What is ViewModelFactory
ViewModelFactory is one of my favourite classes when implementing MVVM. Jeremiah Redekop created this bit of code and I modified it slightly. ViewModelFactory allows us to provide run time view model data and design time view model data. This enables us to have Blendability (ability to see data in Expression Blend as we design our user experiences with a visual tool).
The original version of ViewModelFactory<TViewModel> from Jeremiah provides 2 methods OnLocatedAtRunTime() and OnLocatedAtDesignTime() so that you can separate run time execution (for example querying a REST service) from populating mock data.
I slightly modified ViewModelFactory<TViewModel> to
ViewModelFactory<TViewModel, TDesignViewModel> because I liked the design time implementation to be completely separate from the run time implementation.
The DataContext of the view binds to the ViewModelFactory.ViewModel property, which returns the proper view model based on if it is executing in the designer or runtime.
What’s wrong with my view models
Now that we know what ViewModelFactory does, let’s dive into the problems with my implementation and why we started here in the refactoring.
My View models have a bunch of dependencies like Event Aggregator, access to the data store etc. I was using the Service Locator pattern to resolve these dependencies from a Funq container. The view model gets created from the ViewModelFactory which is created in XAML. I didn’t see a way that I could allow constructor injection of these dependencies so this is why I had put in place Service Locator. I knew it was a bad choice, I just didn’t know what to do about it.
To be able to solve this we had to first fix ViewModelFactory since this is where view models get created.
ViewModelFactory refactor iteration 1
The fun thing when pairing with Glenn especially with refactoring is he has a very iterative approach which avoids an “OMG the whole thing is busted!” situation. It’s also great for learning experiences because you can see step by step the evolution of the changes that are happening.
The first iteration was to introduce resolving dependencies for view models by the ViewModelFactory since it is currently responsible for creating view models.
This is what the ViewModel property looks like for ViewModelFactory before the first iteration of refactoring. As you can see we are simply serving the run time or design time version of the view model.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32||
The first iteration of refactoring we introduced the IoC container in a Service Locator like fashion so that we could then refactor our view models without breaking everything.
This is how it looked after the changes.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24||
This allowed us to now continue to refactor our view models so that they can support constructor injection for their dependencies. This is how a view model looked before the refactoring.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49||
As you can see from this view model the Service Locator is used to resolve dependencies. As you can imagine this code is littered through various view models.
Since we moved the Service Locator code to the ViewModelFactory, now we can shed the container dependency from the view models (remember, we are taking an iterative approach, it gets better later, stick with me).
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50||
Interfaces were introduced because at design time we may need to mock some of these dependencies and not use the real types. We created IDatabase and in the generic parameters of the design time view model there is a design version of the database defined. Since we don’t want to be attempting to talk to isolated storage at design time we can implement a mocked version of the interface which does nothing. The mocked database is pretty simple.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15||
The MainViewModel is far more testable now as well in this version. We don’t need to wire up an IoC container to test it’s functionality. We can inject mock dependencies in the constructor and test the view model easily.
If we wanted to we could also place the design time view models and mocks in a separate assembly.
This is what I had in the App.cs to initialize the Funq IoC container.
|1 2 3 4 5 6 7 8||
Since we are now going to be utilizing the IoC container to construct more of the object graph and inject dependencies into the constructors we need to ensure we are registering all the types we need. While we were at it we decided to reduce the amount of calls to ApplicationHost.Container to position ourselves nicely for when we nuke that sucker all together.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21||
We’ve reduced coupling significantly to the IoC container from the view models and as a result as mentioned things are becoming more testable. We still have the service locator code within the ViewModelFactory but in the next part in this series I will discuss how we created ViewModelResolver and a composition root in the form of a bootstrapper to completely eliminate the ApplicationHost.Container property all together.
Things get much cleaner in part 2, stay tuned!
- Tuning Paxos for high-throughput with batching and pipelining
- Scalable Eventually Consistent Counters
- Create benchmarks and results that have value
- Routing aware master elections
- My new test lab
- Responsible benchmarking
- Understanding hardware still matters in the cloud
- The “network partitions are rare” fallacy
- Messaging and event sourcing
- Further reducing memory allocations and use of string functions in Haywire
- HTTP response caching in Haywire
- Atomic sector writes and misdirected writes
- How memory mapped files, filesystems and cloud storage works
- Hello haywire
- Active Anti-Entropy
- Lightning Memory-Mapped Database
- Write amplification
- Amortizing de-duplication at read time instead of write time
- LevelDB was designed for mobile devices
- AMQP and wire format interopability
- March 2014
- February 2014
- January 2014
- November 2013
- October 2013
- August 2013
- July 2013
- June 2013
- May 2013
- April 2013
- March 2013
- January 2013
- October 2012
- September 2012
- August 2012
- May 2012
- April 2012
- February 2012
- January 2012
- December 2011
- September 2011
- July 2011
- June 2011
- May 2011
- April 2011
- March 2011
- February 2011
- December 2010
- November 2010
- October 2010
- September 2010
- August 2010
- July 2010
- June 2010
- May 2010