Daniel C. Dennett wrote in his 1995 book Darwin’s Dangerous Idea [emphasis author's]
“A skyhook is … an exception to the principle that all design, and apparent design, is ultimately the result of mindless, motiveless mechanicity. A crane, in contrast, is a subprocess or special feature of a design process that can be demonstrated to permit the local speeding up of the basic, slow process of natural selection, and that can be demonstrated to be itself the predictable (or retrospectively explicable) product of the basic process.”
Daniel C. Dennett, Darwin’s Dangerous Idea,1995 (p76)
Basically, a skyhook is a way to explain something without reference to a prior antecedent. Conversely, cranes have explicable antecedents – perhaps until arriving at some primary axiom.
This is a useful analogy in programming, too. Skyhooks are a code smell; indicative of a deeper problem. All skyhooks should be replaced with appropriate cranes.
A skyhook makes your code difficult to mock. Examples of skyhooks are:
- Static methods
- Singletons
- Object construction using
new
- Extension Methods
Each of these make testing more difficult* by hindering your ability to inject mocks into your code, they are skyhooks and thus they are undesirable. Each is used ex nihilo – from nothing.
Thankfully, each of these can be replaced by a suitable crane which will facilitate some kind of external injection (i.e.: used ex materia – from something).
- Interfaces
- Dependency Injection
- Inversion of Control
- Factories
Let’s take an example of each skyhook and change it so that we use hooks instead.
public class UsersModule : IModule
{
public void Initialize()
{
var unityContainer = ServiceLocator.Current.GetInstance<IUnityContainer>();
unityContainer.RegisterType<object, UsersDocumentContent>(
RegionNames.ModuleDocumentContentRegion(ModuleName));
this.currentUser = new User();
}
}
This method contains all four of the aforementioned skyhooks (see if you can spot where before continuing).
Firstly, the use of ServiceLocator
is an example of why the Singleton pattern is my pet hate. I firmly consider it an anti-pattern.
Granted – in this instance – the ServiceLocator
has a SetLocatorProvider
method which unit tests can use to inject the locator, this is a less-than-desirable way of giving a class a service locator. Furthermore, you’ve now given this code the keys to the safe with regard to what services it can acquire. I’d far rather be explicit with the dependencies that a class requires:
public class UsersModule : IModule
{
public UsersModule(IUnityContainer unityContainer)
{
this.unityContainer = unityContainer;
}
public void Initialize()
{
this.unityContainer.RegisterType<object, UsersDocumentContent>(
RegionNames.ModuleDocumentContentRegion(ModuleName));
this.currentUser = new User();
}
private IUnityContainer unityContainer;
}
Now, the specific dependency has been constructor-injected, allowing unit tests to provide a suitable mock.
The second and third skyhooks are similarly extracted:
public class UsersModule : IModule
{
public UsersModule(IUnityContainer unityContainer,
IRegionNameProvider regionNameProvider, IUserFactory userFactory)
{
this.unityContainer = unityContainer;
this.regionNameProvider = userFactoy;
this.userFactory = userFactory;
}
public void Initialize()
{
this.unityContainer.RegisterType<object, UsersDocumentContent>(
this.regionNameProvider.ModuleDocumentContentRegion(ModuleName));
this.currentUser = this.userFactory.CreateNew();
}
private IUnityContainer unityContainer;
private IRegionNameProvider;
private IUserFactory userFactory;
}
I wouldn’t worry about the three constructor parameters – they’ll be injected by your IoC container.
The final skyhook, the RegisterType
extension method is a specialization of the static method skyhook (in as much as extension methods are implemented using static methods, an unfortunate design decision by Microsoft). Thankfully, extension methods must operate only on the public interface of the extended interface. This means that the call will likely result in an equivalent call to a method on the interface that was injected.
In conclusion, whenever you spot a skyhook, replace it with a crane.
* Some mocking frameworks, such as TypeMock, are able to mock skyhooks. However, this should only be considered if the skyhooks are in 3rd-party, unchangeable, code.
An experienced .NET developer, currently working for Nephila Capital Ltd. in Bermuda. Author of "Pro WPF and Silverlight MVVM".