That is it. Now we just need to wire up all the pieces using DI. The Program.cs is what is known as the Composition Root. This is where we wire up everything.
To use the mock data source we code Program.cs as follows:
Code:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
ICoinInfoService coinInfoService = new MockCoinInfoService();
CoinInfoViewModel viewModel = new CoinInfoViewModel(coinInfoService);
frmCoinInfo form = new frmCoinInfo(viewModel);
viewModel.InitialView();
Application.Run(form);
}
}
When you run the app it will display the sample data.
So now we want to display the data from the live source.
We do not need to change anything apart from adding a new implementation for the ICoinInfoService that will get the data from the web:
Code:
public class WebServiceCoinInfoService :
ICoinInfoService
{
public IList<CoinInfoDto> GetTopCoins()
{
List<CoinInfoDto> coins = new List<CoinInfoDto>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://coindata.co.za/api.php");
request.Method = "GET";
using (WebResponse webResponse = request.GetResponse())
{
using (Stream responseStream = webResponse.GetResponseStream())
{
if (responseStream == null)
throw new InvalidOperationException("No response stream");
using (StreamReader responseStreamReader = new StreamReader(responseStream))
{
string jsonResult = responseStreamReader.ReadToEnd();
return JsonConvert.DeserializeObject<List<CoinInfoDto>>(jsonResult);
}
}
}
return coins;
}
}
To now finally run the app with the live data:
change this line in Program.cs:
Code:
ICoinInfoService coinInfoService = new WebServiceCoinInfoService();
Boom and that is it.
So we have completely separated the various layers and concerns and used SOLID principles:
S: Single Responsibility Principle. Each of the classes has one responsibility. The form only does presentation. The ViewMoel gets data and notifies the view that there is new data. The WebService class gets data from the web ect ect.
O: Open Closed Principle. To make changes to the app from displaying mock data to displaying live data, I did not have to change any working code expect for the Composition Root where the components are wired up. I.e. the other classes are Closed for changes. Especially the form and viewmodel. Nothing else needed to change.
L: Liskov's Substitution Principle. I could exchange the mock service and live web service without an issue as they adhered to the same interface contract, the ICoinInfoService .
I: Interface Segregation. Not used here
D: Dependency Injection. I used DI to ctor-inject services into the classes that depended on them. This of course allowed me to inject any compatible service interchangably without changing any code except the wiring up.
This looks like a lot of work for a little app like this but it illustrates the principles. And it is actually not that much work. Took me 10 mins. You just have to plan and get into that mindset. When you have an enterprise level app, these techniques become very important to keep your app extensible. It also allows easy mocking for testing.
In the case where I develop WM6 apps, this works like a charm as all code is shared between WM6 and Windows desktop. Hence I can write and debug all code on desktop without having the painfully slow Code/Deploy/Run/Debug on the device. The only diff between the 2 code bases is the actual form that is laid out for the device instead of the desktop.
Now when you start using DI and dynamically loaded assemblies, that is where it starts to get awesome. Support for different hardware device protocols, easy support for integration with e.g. financial systems, etc etc...
The biggest take-away from this is that SOLID is not specific to WinForms or C#. These techniques can be applied to all stacks and languages.
In the case of MVC with typical WebApi and web page controllers, the real work is deferred to services and domain classes. It is not the controllers' responsibility to do the work. The MVC framework and controllers' only responsibility is to marshall the web request and the routing into a code entry point. Typically the controller should contain minimal code and use DI to get access to a service to do the real work.
Project files VS2015 at
https://www.dropbox.com/s/zbfk9ia3vq4e6bo/MyBBCoin.zip?dl=0