Marker Interface Isn't a Pattern or a Good Idea
The so-called 'marker interface' is a clever hack, and it's understandable why they saw widespread use. But the world came to view it as suboptimal and even a mistake.
Join the DZone community and get the full member experience.
Join For FreeToday, I have the unique opportunity to show you the shortest, easiest code sample of all time. I'm talking about the so-called marker interface. Want to see it? Here you go.
public interface IContainSensitiveInformation
{
}
I told you it was simple. It's dead simple for a code sample, so that makes it mind-blowingly simple for a design pattern. And that's how people classify it — as a design pattern.
How Is This "Marker Interface" Even a Pattern?
As you've inferred from the title, I'm going to go on to make the claim that this is not, in fact, a "design pattern" (or even a good idea). But before I do that, I should explain what this is and why anyone would do it. After all, if you've never seen this before, I can forgive you for thinking it's pretty, well, useless. But it's actually clever, after a fashion.
The interface itself does nothing, as advertised. Instead, it serves as metadata for types that "implement" it. For example, consider this class.
public class Customer : IContainSensitiveInformation
{
public string LastName { get; set; }
public string SocialSecurityName { get; set; }
public string CreditCardNumber { get; set; }
}
The customer class doesn't implement the interface. It has no behavior, so the idea of implementing it is nonsense. Instead, the customer class uses the interface to signify something to the client code using it. It marks itself as containing sensitive information, using the interface as a sort of metadata. Users of the class and marker interface then consume it with code resembling the following:
public void SaveCustomer(Customer customer)
{
if (customer is IContainSensitiveInformation)
_secureService.SaveCustomer(customer);
else
_regularService.SaveCustomer(customer);
}
Using this scheme, you can opt your classes into special external processing.
Marker Interface Backstory
I'm posting code examples in C#, but the marker interface actually goes back a long way. In fact, it goes back to the earlier days of Java, which baked it in as a first-class concept, kind of how C# contains a first-class implementation of the iterator design pattern.
In Java, concepts like serialize and clone came via marker interfaces. If you wanted serialization in Java, for instance, you'd tag your class by "implementing" the marker interface Serializable. Then, third-party processing code, such as ORMs, IoC containers, and others would make decisions about how to process it. This became common enough practice that a wide ecosystem of tools and frameworks agreed on the practice by convention.
C# did not really follow suit. But an awful lot of people have played in both sandboxes over the years, carrying this practice into the .NET world. In C#, you'll see two flavors of this. First, you have the classic marker interface, wherein people use it the way that I showed above. Secondly, you have situations where people get clever with complex interface inheritance schemes and generics in order to force certain constraints on clients. I won't directly address that second, more complex use case, but note that all of my forthcoming arguments apply to it as well.
Now, speaking of arguments, let's get to why I submit that this is neither a "pattern" nor a good idea in modern OOP.
Marker Interfaces Aren't Actually Interfaces
Marker interfaces aren't actually interfaces. At least, they aren't according to the common definition and understanding of the term:
An interface is defined as a syntactical contract that all the classes [implementing] the interface should follow. The interface defines the 'what' part of the syntactical contract and the deriving classes define the 'how' part of the syntactical contract.
An interface defines a contract between implementers and consumers — except a marker interface because a marker interface defines nothing but itself. So, right out of the gate, the marker interface fails at the basic purpose of being an interface.
Like an Open() method that actually closes a connection, the empty interface violates the principle of least astonishment. When you see that a class implements some interface, you think to yourself that it's adopting the behavior of that interface. You probably don't think, "Oh, I'm sure it just does that to signal out of band to some implementer that it should treat this object differently." The marker interface is clever, which confounds users and creates a steep learning curve.
Procedural OOP
Now, at this point, you might say that any design pattern has a learning curve, and that's fair enough. When you have a truly sophisticated or elegant solution, that may create a learning curve worth the price tag. But that's not the case with the marker interface. It confuses while causing other problems.
In the object-oriented world, polymorphism is a powerful concept that can require some acclimation. As a result, people tend to create pseudo versions of it, often using an enum property called "Type" or something similar to differentiate between flavors of an object. The problem here is that doing this forces the decision logic out of the object itself and onto every consumer. The internet has plenty of articles that demonstrate this problem and how to fix it.
The marker interface is really just a variant on this same theme. It's procedural programming in an OO language. Go back up and look at my code example of consuming the marker interface. When I use the IContainSensitiveInformation interface, I force decisions about how to handle this object on every single consumer of it, creating a maintenance headache. This is the same problem as using enums as a poor man's typing system.
Forced Casting (or Reflection)
In a similar vein, this approach creates another issue. I wrote years ago about casting as a polymorphism fail. In it, I quoted Eric Lippert, formerly one of the C# language authors, about casting. He described two flavors of casting and summarized the problems with them this way.
The first kind of cast raises the question "why exactly is it that the developer knows something that the compiler doesn't?"
The second kind of cast raises the question "why isn't the operation being done in the target data type in the first place?"
The marker interface raises the second question since you're using a cast to inquire about the nature of the object. Why aren't you worrying about the business of sensitive information in the object containing it? If you did that, you'd have a single source of truth for the logic, at least in terms of the "what" of the interface. For instance, why not have the interface contain a method called GetEncodedReprestation() or something like that? Then anything implementing it would have to define how to handle sensitive information, instead of leaving it up to clients.
With casting and this sort of introspection are smells, you're not directly using the language to accomplish what you want at compile time, relying instead on out of band knowledge at build time. If you're going to use introspection in this fashion, the .NET framework guidelines tell you to avoid marker interfaces and use attributes instead.
Needless Coupling
Let's look to the wonky world of code statistics for a moment. We've seen that the marker interface forces counterintuitive out of band knowledge and encourages procedural programming. But what about its effect on your code?
At the type level, this promotes the very thing interfaces aim to alleviate: tight coupling. Let's say that, in the broadest terms, I have classes that will contain sensitive data, and I need to define a way to handle that situation. With the marker interface, I spread this handling across three types: the marker interface, the type "implementing" it, and the type deciding how to handle it. Then, as the codebase grows, I may have to define more and more types that understand how to handle it and keep them consistent.
This invites a codebase in which you must perform shotgun surgery. The logic for handling sensitive information leaks out into the broader codebase and spreads itself across multiple types. You now have a tight coupling among these types, with all of them teaming up to handle the concern of sensitive information. With the use of a proper interface, you avoid this problem. The types just understand through the interface that some theoretical means of handling the information exists, and the implementer alone worries about how to handle it.
Marker Interface Isn't a Pattern
Hopefully, by teaming up with a C# language author and the .NET framework guidelines, I've convinced you to stay away from empty/marker interfaces. I've certainly made my case for why I think they're a bad idea. But why do I say that they aren't even a pattern?
Well, people generally define design patterns as solutions to software design problems that you see in the real world. But a marker interface isn't a solution to anything. And I say this not out of contempt for the approach or to try to poison the well. I also don't consider interfaces a design pattern. They just don't rise to that level. Declare a marker interface (or a regular one), and all you've done is define the problem rather than solve it.
So don't worry about whether something is a "design pattern" or not. That categorization tends to elevate a technique almost to the level of Stack Overflow badge, even when the "pattern" isn't a good idea. The fact that some people refer to them as a design pattern or the fact that they saw broad use in the earlier days of Java doesn't mean they make sense for you in 2017 C#. It's a clever hack, and it's understandable why they saw widespread use. But the world came to view it as suboptimal and even a mistake, and now it persists mainly because of massive legacy dependency. Don't make that mistake in your code.
Published at DZone with permission of , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments