12

For example:

Say I have classes A, B, C. I have two interfaces, lets call them IAnimal and IDog. IDog inherits from IAnimal. A and B are IDogs, while C is not, but it is an IAnimal.

The important part is that IDog supplies no additional functionality. It is only used to allow A and B, but not C, to be passed as an argument to certain methods.

Is this bad practice?

  • 8
    IAnimal and IDog are terrible tautology names! – user7519 Jun 13 '12 at 23:41
  • 5
    @JarrodRoberson while I tend to agree, if you work in the .Net world, you are kind of stuck with it (or will be going against the established convention if you do it differently). – Daniel B Jun 14 '12 at 6:17
  • 2
    @JarrodRoberson IMHO MyProject.Data.IAnimal and MyProject.Data.Animal are better than MyProject.Data.Interfaces.Animal and MyProject.Data.Implementations.Animal – Mike Koder Jun 14 '12 at 7:11
  • 1
    The point is there should not be any reason to even care if it is an Interface or Implementation, whether in a repetitive prefix or a namespace either, it is a tautology either way and violates DRY. Dog is all you should care about. PitBull extends Dog doesn't need implementation redundancy either, the word extends tells me all I need to know, read the link I posted in my original comment. – user7519 Jun 14 '12 at 13:08
  • 1
    @Jarrod: Stop complaining to me. Complain to the .NET dev team. If I went against the standard, my fellow devs would hate my guts. – Kendall Frey Jun 14 '12 at 13:10
13

Interface that has no any member or has exactly same members with another interface that is inherited from same interface called Marker Interface and you are using it as a marker.

It is not bad practice but interface can be replaced by attributes(annotations) if the language that you are using support it.

I can confidently say that it is not bad practice because i have seen heavy usage "Marker Interface" pattern in Orchard Project

Here is sample from Orchard project.

public interface ISingletonDependency : IDependency {}

/// <summary>
/// Base interface for services that may *only* be instantiated in a unit of work.
/// This interface is used to guarantee they are not accidentally referenced by a singleton dependency.
/// </summary>
public interface IUnitOfWorkDependency : IDependency {}

/// <summary>
/// Base interface for services that are instantiated per usage.
/// </summary>
public interface ITransientDependency : IDependency {}

Please refer to Marker Interface.

  • Would attributes/annotations support compile-time checking (using C# as a reference language)? I know I could throw an exception if the object doesn't have the attribute, but the interface pattern catches errors at compile time. – Kendall Frey Jun 13 '12 at 22:58
  • If you are using Marker Interface so you haven't got compile check benefit at all. You are basicaly doing type check by interface. – Freshblood Jun 13 '12 at 23:03
  • I'm confused. This 'Marker interface' pattern does provide compile-time checks, in that you can't pass a C to a method expecting an IDog. – Kendall Frey Jun 13 '12 at 23:07
  • If you are using this pattern so you are higly doing reflectional jobs. I mean you are dynamicaly or staticaly doing type checks. I am sure you have something wrong in your use case but i haven't seen how you are using it. – Freshblood Jun 13 '12 at 23:25
  • Let me describe it as I did in the comment on Telastyn's answer. e.g. I have a Kennel class that stores a list of IDogs. – Kendall Frey Jun 13 '12 at 23:26
4

Yes, it's a bad practice in almost every language.

Types are not there to encode data; they're an aid to prove that your data goes where it needs to go and behaves like it needs to behave. The only reason to sub-type is if a polymorphic behavior changes (and not always then).

Since there's no typing, what a IDog can do is implied. One programmer thinks one thing, another thinks another and things break down. Or the things it represents increase as you need more fine grained control.

[edit: clarification]

What I mean is that you're doing some logic based on the interface. Why can some methods only work with dogs and others any animal? That difference is an implied contract since there is no actual member that provides the difference; no actual data in the system. The only difference is the interface (hence the 'no typing' comment), and what that means will differ between programmers. [/edit]

Certain languages provide trait mechanisms where this isn't too bad but those tend to focus on capabilities, not refinement. I've little experience with them so can't speak to the best practices there. In C++/Java/C# though... not good.

  • I'm confused by your middle two paragraphs. What do you mean by 'sub-type'? I'm using interfaces, which are contracts, not implementations, and I'm not sure how your comments apply. What do you mean by 'no typing'? What do you mean by 'implied'? An IDog can do everything that an IAnimal can, but isn't guaranteed to be able to do more. – Kendall Frey Jun 13 '12 at 23:05
  • Thanks for the clarification. In my specific case, I'm not exactly using the interfaces differently. It can probably be best described by saying I have a Kennel class that stores a list of IDogs. – Kendall Frey Jun 13 '12 at 23:23
  • @KendallFrey: Either you're casting out of the interface (bad), or you're creating an artificial distinction that smells of an incorrect abstraction. – Telastyn Jun 13 '12 at 23:29
  • 2
    @Telastyn - Purpose of such as interface is that providing a metadata – Freshblood Jun 13 '12 at 23:36
  • 1
    @Telastyn: So how do attributes apply to compile-time checking? AFAIK, in C#, attributes can only be used at runtime. – Kendall Frey Jun 13 '12 at 23:46
2

I only know C# well, so I can't say this for OO programming in general, but as a C# example, if you need to categorize classes the obvious options are interfaces, enum property or custom attributes. Usually I would choose the interface no matter what others think.

if(animal is IDog)
{
}
if(animal.Type == AnimalType.Dog)
{
}
if(animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
{
}


var dogs1 = animals.OfType<IDog>();
var dogs2 = animals.Where(a => a.Type == AnimalType.Dog);
var dogs3 = animals.Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any());


var dogsFromDb1 = session.Query<IDog>();
var dogsFromDb2 = session.Query<Animal>().Where(a => a.Type == AnimalType.Dog);
var dogsFromDb3 = session.Query<Animal>().Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute),false).Any());


public void DoSomething(IDog dog)
{
}
public void DoSomething(Animal animal)
{
    if(animal.Type != AnimalType.Dog)
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
public void DoSomething(Animal animal)
{
    if(!animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
  • The last section of code is mostly what I use this for. I can see that the interface version is obviously shorter, as well as being compile-time checked. – Kendall Frey Jun 14 '12 at 12:46
  • I have a simillar use case but most .net devs advise strongly against it, but I am not going to use attributes – GorillaApe Mar 5 '15 at 0:01
-1

There is nothing wrong with having an interface which inherits another interface without adding new members, if all implementations of the latter interface will be expected to be able to usefully do things which some implementations of the former may not. Indeed, it's a good pattern which IMHO should have been used more in things like the .net Framework, especially since an implementer whose class would naturally satisfy the constraints implied by the more-derived interface can indicate that simply by implementing the more-derived interface, without having to change any of the member-implementation code or declarations.

For example, suppose I have the interfaces:

interface IMaybeMutableFoo {
  Bar Thing {get;set;} // And other properties as well
  bool IsMutable;
  IImmutableFoo AsImmutable();
}
interface IImmutableFoo : IMaybeMutableFoo {};

An implementation of IImmutableFoo.AsImmutable() would be expected to return itself; an mutable class implementing IMaybeMutableFoo.AsImmutable() would be expected to return a new deeply-immutable object containing a snapshot of the data. A routine which wants to store a snapshot of the data held in an IMaybeMutableFoo could offer overloads:

public void StoreData(IImmutableFoo ThingToStore)
{
  // Storing the reference will suffice, since ThingToStore is known to be immutable
}
public void StoreData(IMaybeMutableFoo ThingToStore)
{
  StoreData(ThingToStore.AsImmutable()); // Call the more-specific overload
}

In cases where the compiler doesn't know that the thing to be stored is an IImmutableFoo, it will call AsImmutable(). The function should return quickly if the item is already immutable, but a function call through an interface does still take time. If the compiler knows that the item is already an IImmutableFoo, it can skip the needless function call.

  • Downvoter: Care to comment? There are times inheriting an interface without adding anything new is silly, but that doesn't mean there aren't times when it's a good (and IMHO underused) technique. – supercat Feb 10 '16 at 19:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.