
Good design is very hard to achieve. It is even harder to come up with a good design before starting to implement the application. We often end up with one big project that has lots of inner dependencies on its own classes. The problem arises when classes rely on concrete implementation of logger class or some static configuration class. Code in these classes looks like this:
public void FooBar() { string foo = Settings.Default.Foo; // some code Logger.Log("bar"); }When you need to move some logic to another project (the most common case is creating a library that implements some specific functionality), you have to break these dependencies. Unfortunately, it will take some time and you will have to change a lot of code.
However, it is possible to avoid this kind of dependencies. You could use some IoC container such as Ninject, without relying on specific implementation of utility classes. Unfortunately, you have to rely on specific IoC container. However, it is very likely that you will use the same IoC container for every project in your solution, so it shouldn't be a problem. After changing the code, it looks like this:
IConfigurationProvider configurationProvider = KernelContainer.Kernel.Get<IConfigurationProvider>(); ILogger logger = KernelContainer.Kernel.Get<ILogger>(); public void FooBar() { string foo = configurationProvider.Foo; // some code logger.Log("bar"); }Obtaining concrete implementation this way may not be the best pattern (compared to just marking properties or constructor with Inject attribute); however, this is the easiest change to make, without the need to instantiate every class through service locator.
Relying on interfaces and not depending on concrete classes will allow you to easily extract parts of your logic to external class libraries.
This style of coding has many more advantages that will be discussed in future lessons.
It's the wood that should fear your hand, not the other way around.