“Are empty interfaces code smell?”

Sedat Kapanoglu
HackerNoon.com

--

I’m one of the earlier users of StackOverflow. Back in 2011, I asked a programming-related question there. Five years, fifty-three upvotes and nine stars later, today, I noticed that it got closed as “primarily opinion-based” and got nominated for deletion too. I was never notified for what had been happening to the content I created. I believed that it would have existed on the web accessible forever. That’s so naive of me of course.

I like StackOverflow a lot, I find it invaluable. I like contributing to it too. I don’t like the moderation culture there but I accept it as it is. I would have appreciated a heads-up though. Giving away my work with a Creative-Commons license for free doesn’t mean that I give no value to it. If it doesn’t adhere to StackOverflow rules, it doesn’t mean that it has no value either. Anyway.

I don’t want that question to be lost. Although it is going to be discarded as “primarily opinion-based” I think there is value in those opinions. I’m moving the question here in case it gets lost. I’m not going to move the answers, because I don’t know if their authors would be okay with it. They might have CC-licensed it, but that doesn’t imply that they are fine with whatever’s happening to their work. Rather, I’m going to mention some of the ideas.

The question was, as you guessed, “Are empty interfaces code smell?”. Here is the full text:

I have a function that returns same kind of objects (query results) but with no properties or methods in common. In order to have a common type I resorted using an empty interface as a return type and “implemented” that on both.

That doesn’t sound right of course. I can only console myself by clinging to hope that someday those classes will have something in common and I will move that common logic to my empty interface. Yet I’m not satisfied and thinking about whether I should have two different methods and conditionally call next. Would that be a better approach?

I’ve been also told that .NET Framework uses empty interfaces for tagging purposes.

My question is: is an empty interface a strong sign of a design problem or is it widely used?

EDIT: For those interested, I later found out that discriminated unions in functional languages are the perfect solution for what I was trying to achieve. C# doesn’t seem friendly to that concept yet.

The question was not worded the best way to say the least. It was ambiguous. I needed to answer a couple of follow-up questions to clarify my problem. I received valuable comments and answers. I accepted an answer too which mentions “Marker Interfaces” because it was the most elaborate opinion about the subject, with quotes from articles and links.

I have been okay with that empty interface for a long time, because “Marker Interface” man, yeah, until a couple weeks ago. Something clicked with me and I found out the problem with my code in a euphoric eureka moment. Yes, it turned out that that interface was in fact a code smell, and I finally figured out the correct design for it.

Let me clarify the problem first. I’m going to try to give a very simplified example: Twitter. We are writing Twitter from scratch, in C#! (I like C#). We have Web and DB layers in separate projects. We have a search query function in the DB layer:

IQueryResult Query(string input);

Depending on what user types in the search box, the query function returns a different IQueryResult instance. If it starts with “@” it returns a UserProfileResult. If it is a regular text it returns SearchQueryResult. If it is empty it returns ValidationErrorResult.

Depending on the result object type our controller in the Web layer does different things. If it’s a ValidationErrorResult it just renders a standard validation error, does nothing else. If it’s a UserProfileResult it redirects to the user’s profile. If it’s a SearchQueryResult it redirects to the search page.

The problem was that neither of the types had a common property or functionality with each other. ValidationErrorResult had ErrorMessage, UserProfileResult had UserId, SearchQueryResult had Keyword. So I ended up with this:

interface IQueryResult
{
}

That’s it. It was just a guilt-ridden hack I came up with, because I didn’t dare to have this abomination:

object Query(string keyword);

I had a teeny-weeny bit of type safety. I could be sure that Query could not return anything other than an object that implemented IQueryResult. But in the end the web layer code turned out like this:

var result = db.Query(input);
if (result is ValidationErrorResult)
{
var validationResult = (ValidationErrorResult)result;
return RenderError();
}
if (result is SearchQueryResult)
{
var searchResult = (SearchQueryResult)result;
return Redirect("Search", new { q = searchResult.Keyword });
}
if (result is UserProfileResult)
{
var profileResult = (UserProfileResult)result;
return Redirect("Profile", new { user = profileResult.UserId });
}

But you see, they were all lies. Of course Query method could start returning a new IQueryResult type and I wouldn’t have handled it. I did not have any type-safety but the illusion of it.

I was bothered by those sequential type checks and casts too. You could optimize them slightly with an “as” operator and a null check but it’s a sequential check regardless. Every time a Twitter user requests, you would be doing that extra work, unnecessarily. That’s not proper object-oriented programming, that’s not efficient, not fast, not flexible yet prone to errors. What was wrong?

I wanted to have that illusion back then because DB and Web layers were isolated from each other. I couldn’t have implemented a “RenderError” in ValidationErrorResult in a DB layer. It didn’t have access to anything Web had. I couldn’t have implemented a Redirection either. DB didn’t know about those things. I had to stick with those ugly “is” checks. Terrible.

Object-oriented programming is mostly about virtual dispatch. We shouldn’t be looking up stuff to reach to the correct code. That’s why we have nifty features like inheritance and polymorphism. Otherwise why bother, right?

An interface is like an outlet. It runs whatever you plug into it as long as that thing supported the outlet’s standard. So what I needed was, in fact, an outlet in the DB layer which I can plug in later with whatever functionality I wanted. Something like this:

interface IQueryResult
{
void Visit(IQueryResultVisitor visitor);
}
interface IQueryResultVisitor
{
void VisitValidationError(ValidationErrorResult result);
void VisitUserProfile(UserProfileResult result);
void VisitSearchQuery(SearchQueryResult result);
}
class UserProfileResult : IQueryResult
{
void Visit(IQueryResultVisitor visitor)
{
visitor.VisitUserProfile(this);
}
}
class ValidationErrorResult : IQueryResult
{
void Visit(IQueryResultVisitor visitor)
{
visitor.VisitValidationError(this);
}
}
class SearchQueryResult : IQueryResult
{
void Visit(IQueryResultVisitor visitor)
{
visitor.VisitSearchQuery(this);
}
}

Finally I re-invented the “Visitor Pattern”. Eureka!

Of course I knew about the visitor pattern. I had used it in other places. But I never subconsciously linked it to the type of problem I had here. One reason was that I somehow didn’t think that I could get a return value from those void functions. Then I remembered that objects contained state too. So my controller code would be like this:

var result = db.Query(input);
var visitor = new QueryResultVisitor(this.Context);
result.Visit(visitor);
return visitor.Result;

I didn’t need the result in the interface signature. I just needed it in the implementation:

class QueryResultVisitor: IQueryResultVisitor
{
ActionResult Result { get; private set; }
private IControllerContext context; QueryResultVisitor(IControllerContext context)
{
this.context = context;
}
void VisitValidationError(ValidationErrorResult result)
{
this.Result = context.RenderError(result.Message);
}
void VisitUserProfile(UserProfileResult result)
{
this.Result = context.Redirect("Profile", new { user = result.UserId });
}
void VisitSearchQuery(SearchQueryResult result)
{
this.Result = context.Redirect("Search", new { q = result.Keyword });
}
}

It is much less cumbersome than it looks. Well, it is as much as it looks obviously. But it’s not a mood-killing mundane work. Especially when you think of benefits while implementing: Your DB classes suddenly start to support different clients with different behavior patterns. You get the benefit of compile-time checks that force you to implement missing parts when you add a new result type. Your controller code gets simplified down to a couple of lines. It is a fast, scalable mapping operation.

More importantly, your awkward empty interface suddenly becomes functional. Yay! The elephant in the room has gone for good.

For the sake of completeness, we could have solved the same problem with discriminated unions in a functional programming language, like Haskell or F#. I’m rusty on the syntax but it would come down to this F#-looking-code:

type QueryResult = 
| ValidationError of string
| SearchQuery of string
| UserProfile of int
let Query text : QueryResult = ... query code ...

And our controller code becomes something like this:

let result = db.Query input
match result with
| ValidationError v -> RenderError v
| SearchQuery s -> Redirect "Search" dict["q", s]
| UserProfile p -> Redirect "Profile" dict["user", p]

It involves much less boilerplate, is trivial to understand and provides better compile-time checks. Differently from “switch” statements in imperative languages, if you added a new result type, the compilation would fail because of the unhandled case. Not sure if it would be as fast, but would involve much less wiring for sure.

I couldn’t bring myself to use functional languages in production yet, except XSLT which doesn’t count. Such switch is quite a big leap with lots of gotchas. I’m at least glad that I now have a satisfactory solution to those empty interfaces in the domain of imperative languages. I can finally bring closure to my question and keep it away from the reach of the hammer of moderation. If you enjoyed it too, even better!

TL;DR: If you end up with an empty interface or generic return types, see if it’s an opportunity to use the visitor pattern.

P.S. Although I got triggered by moderators this time, I’m considering moving other material I wrote on programming from other platforms with similar extended commentary to Medium. Let me know of if that sounds like a good idea!

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.

To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

--

--

Sedat Kapanoglu
HackerNoon.com

Author of Street Coder (https://streetcoder.org) · Ex-Software Engineer at Microsoft · Founder of eksisozluk.com · Demoscene old-timer