Interface Tagging?

Started by
12 comments, last by Telastyn 11 years, 10 months ago
I recently had a dispute over the use of empty (or otherwise duplicated) interfaces/abstract classes to 'tag' a class.

What do you guys think? Useful trick or bad practice?

I gave my argument in the context of mainstream languages that can even do this sort of thing (C++/C#/Java), but a wider discussion is fine by me.
Advertisement
I find that to be a useful trick. If it helps you or the others in the discussion, you could research the <a href="http://en.wikipedia.org/wiki/Marker_interface_pattern">marker interface pattern</a>. I imagine there are quite a few positive and negative viewpoints out there on the topic.
I sometimes use empty interfaces to indicate a 'grouping' of classes without actually sharing any implementation between those classes. This is kind of a code maintenance aid for me when I want to keep track of how things are intended to be related, but without having a common RUNTIME interface (i.e. "find all references" is better than find-text-in-comments).

I only use empty interfaces when it *doesn't* make sense to use an ABC or non-empty interface.

Since you can add and remove them without really affecting any code, I don't think there are any real downsides.
I'm curious what the "counter" argument was, obviously made by your unnamed assailant?

As for marking, I find it quite useful but the use cases tend to be rather rare. There is, in my opinion, a difference between a true marker interface and an interface that is, for whatever reason, simply empty.
I would find it pointless. If two classes don't have anything actually in common, why should they share a class? If its a placeholder I might see the benefits; that you may add shared attributes between those classes in the future.

Its mostly a matter of taste though, as far as I can tell. There wouldn't be any performance issues of an empty class.

A prolific use of classes that are badly conceptualized is however a bad thing. Without knowing the context of your particular case, I'd say the extra empty class and the two inheriting from it would be evidence of this.

I like OO structures, but they are usually abused and overused. Don't use inheritance where it's not called for.

I'm curious what the "counter" argument was, obviously made by your unnamed assailant?


No, I was actually on the bad practice side.

To me, they can only end in badness. If you have an empty interface, and a method takes that interface you can't actually do anything without: casting, "if X is T" stuff, reflection, or type trait shennanigans.

  • Casting out of an interface is bad. If you knew you were getting a meaningful type, then say that. If you might get multiple types... well that leads to #2:
  • "If X is T" is bad because it violates the open closed principle. You can put your marker interface on things, but you can't actually do things with it until you modify the giant "If X is T" block.
  • Reflection is reflection. To be avoided. Plus, languages with reflection have attributes for this sort of thing.
  • Type traits work in C++, but why not just use the concrete type with the trait at that point?

If you have a non-empty interface that is identical to another except for the name, then you have different issues. Either they:

  • Do different things with the same name; in which case you need better names. If one ToString is human readable and another is a serialization string... maybe you should be clearer.
  • Behave differently due to the type name. This is the worst case, and (as is my understanding) where this pattern is most used. The issue here is that you're using the type system to do everything except enforce behavior. That's what it is there for!The behavior differences don't exist in the marker, they exist in the implementors. At that point, nothing but social contract enforces that a certain marker is used as expected; worse yet, those expectations are often based on the real world behavior of the type name. These behaviors are assumed by developers, and will be different depending on their personal expectations/experiences.

Again, I made my argument against C++/Java/C#. Something like Scala with actual traits behave differently. Hell, I use this sort of thing in Tangent coding, but that's because Tangent allows me to compose types on the fly so that tags can be applied partially (and easily).
Can someone give examples of in what kind of situation you could use interface tagging?

o3o


Can someone give examples of in what kind of situation you could use interface tagging?


This was provided in the original discussion:

http://orchard.codeplex.com/SourceControl/changeset/view/9eeaf6cf48d2#src/Orchard/IDependency.cs

[quote name='Kyan' timestamp='1339654633' post='4949058']
I'm curious what the "counter" argument was, obviously made by your unnamed assailant?


No, I was actually on the bad practice side.
[/quote]

And I'm with you. I won't repeat your bullet points though.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
"Tag" interfaces are a code smell in most cases. I'm sure someone could come up with some template trait hackery where they are used and I wouldn't really be able to fault it, but that's rare in my experience. Most of the time tag interfaces are incorrect attempts at misusing upwards casts in an inheritance hierarchy in a misguided effort to be type safe, at least from what I've seen. If your types have something in common, it probably isn't an empty interface.

Tag classes - by which I mean classes which are empty but not derived from - are fine IMHO. I use them for things like compile-time type dispatch instead of runtime (potentially virtual) dispatch. For instance:

struct FooTag { };
struct BarTag { };

struct Operations
{
void Frobnicate(const FooTag& tag)
{
std::cout << "Do some foo!\n";
}
void Frobnicate(const BarTag& tag)
{
std::cout << "Do some bar!\n";
}
};


Saves a switch on an enum or something similar, and in certain code organizational patterns can be handy. N.B. that nothing derives from the tag objects, they're just dummy types used to tell the compiler that a Foo isn't a Bar.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement