I was told by someone that if you put a picture of a cat in your blog, you’ll get instant site views to the sound of “oos” and “aahs”. Unfortunately since the majority of my demographic is probably non-cat loving males, I’m not hedging my bets.
I thought I’d put this post up more for my benefit as a tutorial on interfaces, but I hope others will find it useful. The other reason is that although I’ve seen answers on the internet around this topic, I’ve not really been satisfied with the content which can lack code example code (possibly for the right reasons).
What is a Circular Dependency?
A circular dependency is when 2 libraries need access each other’s code ie each assembly is dependent on each other.
The first thing to say is that if you have a circular dependency, don’t have a circular dependency. You either need to
1. Extract the Code into a separate shared library
2. Merge the 2 libraries or relevant classes together
I’ve seen both answers which seem to be the easy ones and as a disclaimer I’m advocating you use the above 2 approaches all the time. Indeed there is a strong argument to have fewer assemblies and splice your code based on units of deployment rather than as a way of structuring your code (which you can easily use namespaces for). However I’m sure there have been times when you just need those damn libraries to talk to each other. Enter Interfaces.
I love Interfaces but it’s one of those things you won’t get initially. I know it took me some time to truly understand their beauty. You hear these words about “decoupling code” and “Contracts” but it took a while for me to get it. I’ll try to explain Interfaces as briefly as I can.
Interface describe the essence of classes which share similar traits, like inheritance but a more purer form. Interfaces define the shape of all or part of the object which implements it. By shape I mean it’s signature or state but not the concrete implementation of it. Methods, properties, events and indexers can be used within Interfaces but you leave or delegate the fleshing out within the class which implements the interface i.e the actual code to explain the method is within the class which implements the interface. Indeed Interfaces are to classes what delegates are to methods. Just like with delegates where you have to have the same method signature in order for your code to compile, with Interfaces, each class implementing it must have the same shape (methods, properties, events etc) defined within the interface. Also when you define Interfaces, think of methods/verbs (although you can have properties/nouns).
The question to ask is what do I want this class to do which other similar classes will need to do now and in the future?
So what’s the point? The biggest question I used to ask myself too. Interfaces are really cool in adding an extra layer between classes (so called decoupling). Where you have the potential for another similar class to be used in the future, you should define an interface.
For example I’m looking at prototyping an application which I know will need a secure login to EMIS (they are a supplier of software to doctors in the UK). I define an Interface called ILogin which contains a login method (DoLogin(username, password)).
– At the moment I’m pointing the instantiation of this class to my dummy login class which implements ILogin.
– In the future I’ll create a full blown EMIS Login class, but the beauty is that I’ve already defined my methods which I will use in ILogin, so all I need to do is create the same method within the EMIS Login class and write code specific for EMIS without worrying about linking it to the main code. The interface does this for me.
Let’s also say I want to develop software for another company, I just implement the ILogin Interface for this company and flesh out the body within the DoLogin(username, password) of the class for this company. Whenever I get to instantiate the Login Class, I can instantiate ILogin instead eg ILogin login = new EMISLogin; This way the complier will know which class to instantiate when creating the Login Object.
Taking the above example further, Interfaces are really useful for Unit Testing when you want to create a Mock Object to imitate the real external dependency and need to find an easy way to do this. Once you are happy with the mock, just point the instantiation of the interface to the real dependency instead of the mock and you are away.
You can also do a foreach loop around the Interfaces so classes of totally different content can be iterated. Of course you have to be careful of casting to type ofs but it can be a useful feature.
So my advice is to..
use Interfaces where there is the potential in the future for your content to change but where your structure (or shape) remains
So that’s how I use interfaces; now on to how you use interfaces to solve circular dependencies.
Class Recipient and Class Donor need to access each other’s methods. They are both in different libraries/projects.
This is the end point where we need to get to. We need to create a middle library which both recipient and donor can see which will house our Interface. I’ve called it the Broker.
– Recipient can see/has reference to the Broker and the Donor
– Donor only has reference to the Broker
The recipient method can merrily access the Donor method but you’d need to go thorough IBroker to get access to the recipient’s method.
Step 1 Create the IBroker Interface. This must have the same method signature as the method you want passed
Step 2 In the Recipient Class, Implement IBroker and do your thing with this method
Step 3 In your Donor inject the IBroker (Donor can see Broker as it has a reference to it but it can’t see Recipient) in the constructor. Once you have done this and IBroker is a member variable, you can then access it’s public interface method.
Step 4 You can then access the method within Donor Class even though it can’t see the Recipient Class. You naturally need to instantiate a Recipient Method so the Interface knows which method to access when queried.
These are the calls from the main class (which is also within a separate library).
Hope you found this useful.