Marker interfaces tend to be one of those things that your don’t think about often. But when you have a problem they can help you solve, you think they are awesome. The trick is knowing when they should and should not be used.
What is a Marker Interface?
Marker interfaces are basically an interface contract with no methods or properties specified. The notion of interface contracts are common in many languages but take on many different names. If you’re familiar with Java interfaces, Objective-C protocols, C++ pure abstract classes, or any similar technique in your language of choice, you know what I’m referring to.
Typically, an interface contract specifies a set of methods that must be implemented by any object that intends to support the contract. This enables you to refer to an object by the contract (which typically corresponds to a functional role or feature role in the program) rather than the concrete type of the object itself. This both clarifies how code consuming an object intends to use the object and makes it possible to easily use different concrete classes that also implement the contract without needing to change the consuming code. The consumer doesn’t care about the concrete object type as long as it satisfies the contract.
So why on Earth would you ever want to create a contract that doesn’t specify any methods? Most of the time, you don’t. But if you need a way to abstractly specify an object type, especially when the abstract type may have concrete instances that don’t share a meaningful inheritance chain, then marker interfaces are likely a perfect solution. You most often have a need to specify an abstract object type like this when you have orchestration or coordination code that is transferring objects from one part of a program to another. The orchestration code doesn’t need to use or modify the object, it just needs to move it from one place to another.
Such orchestration code tends to be both fairly abstract and highly leveraged within an application’s architecture, so you often don’t want to specify concrete types for the objects being sent back and forth. A simple way of generically passing objects is to specify the parameters of methods in the orchestration code as generic objects. But specifying transfer method parameters as a generic object has a few downsides. Firstly, it doesn’t provide any guidance to the caller about what should be passed. Secondly, literally anything can be passed. If you accidentally pass an object you didn’t intend to, the compiler/parser won’t catch the error.
void transfer(Object data)
Marker interfaces help avoid these problems.
By using a marker interface, you can specify the intent of what should be provided to the method without restricting you to a specific type or inheritance chain.
void transfer(IFooDataObject data)
This method signature is much clearer to the person using the method what type (in the functional intent sense of the word) of object should be passed to the method. Any concrete type can still be passed, but you have to make a conscious choice about which objects should be allowed to be passed by adding the marker interface to the type definition of the object before you can pass an object of that type. In a mature architecture, prior decisions about which objects carry this declaration can greatly minimize confusion about what to send where.
You also get a level of type checking via the compiler, so if you accidentally pass a reference to a UI control object instead of the data property on the control you intended to pass, the compiler can let you know. Without the marker, it wouldn’t.
This technique of using marker interfaces for high-level type specification is really the one application of marker interfaces I use frequently. I tend to not like using marker interfaces to direct flow of control within an application (i.e. “if foo implements bar then do x”) because I think such control flow is typically better governed by object state than by type. But that thought is probably worth a post of it’s own.