In some environments, using dependency injection (DI) and inversion of control (IoC) are not only a nice-to-have, but an expected requirement. And while DI and IoC can provide a nice abstraction away from your concrete classes, there are times, when working in a brownfield application for example, that you might find yourself in the position of simply not having the luxury of time to wire in one of these frameworks. However, that does not mean that we can not strive to accomplish what these frameworks aim to provide for us: loose coupling.
So with that in mind, I want to share a method of loosely coupled classes by way of reflection. And in the spirit of three letter acronyms (TLA), I call this pattern reflection of control (RoC).
In a nutshell, the idea is that we will write a program that will take some action on all of the classes that implement our interface. However, we do not want our program to have intimate knowledge about every class that implemented our interface. Instead, we just want to write a class that implements an interface and let some magic happen.
To get started, I will first define an interface, ISomeInterface. ISomeInterface is not a complex interface at all. It simply defines a method called GetMessage() that returns a String.
11 12 13 14 |
public interface ISomeInterface { String GetMessage(); } |
Next, I am going to create two classes that each implement ISomeInterface. For my purposes, I have created each class as a singleton, and for kicks, I have made them thread safe by using the double-checked locking pattern. Nothing fancy about each of these classes.
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 |
public class Class1 : ISomeInterface { private Class1() { } private static volatile Class1 _instance; private static readonly Object _lock = new Object(); public static Class1 Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new Class1(); } } } return _instance; } } private String Data { get { return "Class1 is ready."; } } public String GetMessage() { return Data; } } |
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 |
public class Class2 : ISomeInterface { private Class2() { } private static volatile Class2 _instance; private static readonly Object _lock = new Object(); public static Class2 Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new Class2(); } } } return _instance; } } private String Message { get { return "Class2 has a message"; } } public String GetMessage() { return Message; } } |
Each implements the GetMessage() method by returning a private property that contains each class’ specific message. And to help illustrate that each class can be different, they each retrieve a different property in the GetMessage() method call. Class1 will return Data and Class2 will return Message.
Now that I have laid the groundwork, let’s get to the fun part.
Introducing SomeInterfaceFactory. Remember when I stated what the idea was for our program above? Well here it is.
11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Program { static void Main(string[] args) { foreach (var intf in SomeLibrary.SomeInterfaceFactory.SomeInterfaceInstances) { Console.WriteLine(intf.GetMessage()); } Console.WriteLine("Press ENTER to quit: "); Console.ReadLine(); } } |
It is simple and clean. But what voodoo magic is SomeInterfaceFactory doing? Let’s take a look.
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
public class SomeInterfaceFactory { private static volatile IList _someInterfaceInstances; private static readonly Object _lockSomeInterfaceInstances = new Object(); public static IList SomeInterfaceInstances { get { if (_someInterfaceInstances == null) { var someInterfaceInstances = SomeInterfaceTypes.Select(CreateInstance).ToList(); lock (_lockSomeInterfaceInstances) { if (_someInterfaceInstances == null) { _someInterfaceInstances = someInterfaceInstances; } } } return _someInterfaceInstances; } } private static volatile IList _someInterfaceTypes; private static readonly Object _lockSomeInterfaceTypes = new Object(); private static IList SomeInterfaceTypes { get { if (_someInterfaceTypes == null) { var someInterfaceTypes = new List(); foreach (var types in AppDomain.CurrentDomain.GetAssemblies().Select(assembly => assembly.GetTypes())) { someInterfaceTypes.AddRange( types.Where(type => type.GetInterface(typeof(ISomeInterface).ToString()) != null) .ToList() .OrderBy(type => type.FullName)); } lock (_lockSomeInterfaceTypes) { if (_someInterfaceTypes == null) { _someInterfaceTypes = someInterfaceTypes; } } } return _someInterfaceTypes; } } private static ISomeInterface CreateInstance(Type t) { if (t == null) { throw new ArgumentNullException("t", "A type of ISomeInterface is required!"); } return Activator.CreateInstance(t, true) as ISomeInterface; } } |
Now, if you download the source, you will see that this factory is similar to the singletons above in that it too uses double-checked locking. However, I want to focus on two areas of interest here.
The first is the property SomeInterfaceTypes. This property’s fun begins at line 47. Here, using the current AppDomain, we get all of the loaded assemblies* and for each assembly, we get all of the types defined in that assembly. Then starting on line 49, we get all of the types that implement our interface and we add them to an IList.
The next bit of interest happens in SomeInterfaceInstances on line 23. Here, we call a method CreateInstance() for each type returned by SomeInterfaceTypes. CreateInstance is wrapping a call to Activator.CreateInstance(Type, Boolean). The boolean here is important since our singletons each have a private constructor. By passing in a true value, we are telling Activator.CreateInstance to match the non-public constructor if necessary, which it is in this case.
When we run our program, we should see the output from the GetMessage() calls from each our of singletons.
1 2 3 |
Class1 is ready. Class2 has a message Press ENTER to quit: |
Fantastic! We literally only provided a class. Nowhere are we calling either concrete class’ singleton instance to do anything. Not even our factory knows about the classes. Think about that for moment. The factory does not even know about the implementations. It discovers them at run time. Now imagine if we had several of these. Maybe they do something more meaningful than displaying a message like in this example. For example, maybe our interface defines a caching contract that we can use get cache status from and/or manage objects that cache data. It would be nice to have a way to manage all of those types without needing to manually manage a list of those types anytime we add or remove a type from our codebase.
So that’s it. Nothing more to see here. No really, we should probably just end on a high note. Hey, is that Game of Thrones?
OK fine, we can discuss the weaknesses, too.
Did you catch that asterisk up above? Those pesky asterisks. Why would I ruin a seemingly nice write-up with an asterisk. Well, because unfortunately, there’s a catch. There’s always a catch.
If you have downloaded the source, you may have noticed that our interface, singletons, and factory classes are all in the same library, named SomeLibrary.
But what if they were not? Let’s add another library and see what happens.
Maybe I am being pesimistic, but I have named my new library UnknownClassLibrary1 with a new singleton named UnknownClass1. One of these days, I am going to pick better class and libary names. Until then …
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 |
public class UnknownClass1 : ISomeInterface { private UnknownClass1() { } private static volatile UnknownClass1 _instance; private static readonly Object _lock = new Object(); public static UnknownClass1 Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new UnknownClass1(); } } } return _instance; } } private String MysteryData { get { return "UnknownClass1 is ready but we'll never know."; } } public String GetMessage() { return MysteryData; } } |
As you can see, this new class is similar to our other singletons, except that it’s implementation of GetMessage() returns a property named MysteryData.
Now, let’s run our program again …
1 2 3 |
Class1 is ready. Class2 has a message Press ENTER to quit: |
Hey, wait a minute! Where is UnknownClass1’s message?
Well, unfortunately, it has to do with that pesky little line 47 in our factory. You know the one. The one with the call to GetAssemblies(). You see, sometimes, things can be smarter than we want them to be. And in this case, we can thank our compiler.
Because UnknownClass1 is in a library that has not yet had anything called, it is never loaded and therefore the library is not included in our compiled code even though our project references the library. And since it is neither loaded nor included, using GetExecutingAssembly is not going to help either. Of course, we do have the option of manually loading the assembly by name but that defeats the purpose of what we original set out to solve. This is where Yosemite Sam might have some choice words about the compiler being smarter than he was by optimizing the resulting code.
Need proof? Using our old friend ILSpy to see what’s what, we can see that the SomeLibrary assembly does not contain a reference to UnknownClassLibrary1 and neither does our main application.
To resolve this and see it in action, I have added another class to UnknownClassLibrary1 named UnknownClassFinder.
11 12 13 14 15 16 17 |
public class UnknownClassFinder { public static DateTime FindIt() { return DateTime.Now; } } |
And I have added a call in our main program to UnknownClassFinder’s FindIt() method.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Program { static void Main(string[] args) { var dt = UnknownClassLibrary1.UnknownClassFinder.FindIt(); foreach (var intf in SomeLibrary.SomeInterfaceFactory.SomeInterfaceInstances) { Console.WriteLine(intf.GetMessage()); } Console.WriteLine("Press ENTER to quit: "); Console.ReadLine(); } } |
And now when we run the program…
1 2 3 4 |
UnknownClass1 is ready but we'll never know. Class1 is ready. Class2 has a message Press ENTER to quit: |
Ah, there it is.
OK, so if you only have classes that implement your interface in their own library, and nothing is directly used in those libraries, then yeah, this pattern falls short. However, if you have a library that’s used directly, at least once, then this pattern might be helpful to you, especially in a brownfield application.
Now of course, someone is going to point out that I am using reflection. So in the spirit of full disclosure regarding this pattern’s negatives, the performance impact that comes with reflection may be something you are not willing to accept.
However, in the spirit of loosely coupled classes, the beauty of this is that it is difficult to get much looser than this. Like I said before, NOTHING is directly calling these classes, not even the factory.
Download the examples discussed in this article here.