tags:

views:

85

answers:

5

Hi.

I have two basic interface-related concepts that I need to have a better understanding of.

1) How do I use interfaces if I only want to use some of the interface methods in a given class? For example, my FriendlyCat class inherits from Cat and implements ICatSounds. ICatSounds exposes MakeSoftPurr() and MakeLoudPurr() and MakePlayfulMeow(). But, it also exposes MakeHiss() and MakeLowGrowl() - both of which I don't need for my FriendlyCat class.

When I try to implement only some of the methods exposed by the interface the compiler complains that the others (that I don't need) have not been implemented.

Is the answer to this that I must create an interface that only contains the methods that I want to expose? For example, from my CatSounds class, I would create IFriendlyCatSounds? If this is true, then what happens when I want to use the other methods in another situation? Do I need to create another custom-tailored interface? This doesn't seem like good design to me.

It seems like I should be able to create an interface with all of the relevant methods (ICatSounds) and then pick and choose which methods I am using based on the implementation (FriendlyCat).

2) My second question is pretty basic but still a point of confusion for me. When I implement the interface (using Shift + Alt + F10) I get the interface's methods with "throw new NotImplementedException();" in the body. What else do I need to be doing besides referencing the interface method that I want to expose in my class? I am sure this is a big conceptual oops, but similar to inheriting from a base class, I want to gain access to the methods exposed by the interface wihtout adding to or changing them. What is the compiler expecting me to implement?

-- EDIT --

I understand #1 now, thanks for your answers. But I still need further elaboration on #2. My initial understanding was that an interface was a reflection of a the fully designed methods of a given class. Is that wrong? So, if ICatSounds has MakeSoftPurr() and MakeLoudPurr(), then both of those functions exist in CatSounds and do what they imply. Then this:

public class FriendlyCat: Cat, ICatSounds
{

...

public void ICatSounds.MakeLoudPurr() 
{
 throw new NotImplementedException();
}

public void ICatSounds.MakeSoftPurr()
{
 throw new NotImplementedException();
}

}

is really a reflection of of code that already exists so why am I implementing anything? Why can't I do something like:

FriendlyCat fcat = new FriendlyCat();
            fcat.MakeSoftPurr();

If the answer is, as I assume it will be, that the method has no code and therefore will do nothing. Then, if I want these methods to behave exactly as the methods in the class for which the interface is named, what do I do?

Thanks again in advance...

+1  A: 
  1. You have to implement all the methods in your interface. Create two interfaces, IHappyCatSounds and IMeanCatSounds, split out those methods. Don't implement IMeanCatSounds in FriendlyCat, because a friendly cat is not a mean cat. You have to think about an interface as a contract. When you write the interface, you are guaranteeing that every class that implements the interface will have those members.

  2. It throws a NotImplementedException because you haven't implemented it yet. The compiler is expecting you to implement the code that would be completed when the cat purrs, meows or hisses. An interface doesn't have code in it. It's simply nothing more than a contract for any class that implements it, so you can't really "access the code" the interface implements, because the interface doesn't implement any code. You implement the code when you inherit from the interface.

For example:

// this is the interface, or the "contract".  It guarantees
// that anything that implements IMeowingCat will have a void
// that takes no parameters, named Meow.
public class IMeowingCat
{
    void Meow();
}

// this class, which implements IMeowingCat is the "interface implementation".  
// *You* write the code in here. 
public class MeowingCat : IMeowingCat
{
    public void Meow
    {
        Console.WriteLine("Meow.  I'm hungry");
    }
}

I'd strongly suggest picking up a copy of The Object Oriented Thought Process, and read it through in it's entirety. It's short, but it should help you to clear things up.

For starters, though, I'd read this and this.

David Morton
Thanks David, I get #1 now but could you please see my edit for #2. I am still having trouble here.
dushkin
Yes, your understanding is wrong. An interface is not a reflection of the fully-designed methods of the class. It is nothing more than a contract, or a spec. It's like someone saying "build me a car that turns left". Great, you can "say" turn left, but you need to actually implement the method that turns left. The interface defines what the class 'will do', and the code in the implementing class defines 'how it will do it'. Chances are it doesn't accomplish a cat's purr by throwing an exception. Did you read the links I posted above, especially the second one?
David Morton
Thanks David. I read the first yesterday but felt it didn't really fill in the blanks for me. I am reading number two now and it has been much more helpful. I'll follow up when I am done if I have other questions. Thanks again.
dushkin
+2  A: 

An interface is a contract. You have to provide at least stubs for all of the methods. So designing a good interface is a balancing act between having lots of little interfaces (thus having to use several of them to get anything done), and having large, complex interfaces that you only use (or implement) parts of. There is no hard an fast rule for how to choose.

But you do need to keep in mind that once you ship your first version of the code, it becomes a lot more difficult to change your interfaces. It's best to think at least a little bit ahead when you design them.

As for implementation, it's pretty common to see code that stubs the methods that aren't written yet, and throws a NotImplemented exception. You don't really want to ship NotImplemented in most cases, but it's a good get around the problem of not having the code compile because you havn't implemented required parts of the interface yet.

John Knoeller
+2  A: 

There's at least one example in the framework of "deliberately" not implementing all of an interface's contract in a class: ReadOnlyCollection<T>

Since this class implements IList<T>, it has to have an "Insert" method, which makes no sense in a read-only collection.

The way Microsoft have implemented it is quite interesting. Firstly, they implement the method explicitly, something like this:

public class ReadOnlyCollection<T> : IList<T>
{
    public void IList<T>.Insert(int index, T item)
    {
        throw new NotSupportedException();
    }

    /* ... rest of IList<T> implemented normally */
}

This means that users of ReadOnlyCollection<T> don't see the Insert method in intellisense - they would only see it if it were cast to IList<T> first.

Having to do this is really a hint that your interface hierarchy is a bit messed up and needs refactoring, but it's an option if you have no control over the interfaces (or need backwards compatibility, which is probably why MS decided to take this route in the framework).

Matt Hamilton
Interesting. Why the downvote? I'm not saying that dushkin *should* do this - I'm just pointing out that MS do it themselves in the framework. It's definitely an option if he has no control over the interfaces he needs to implement.
Matt Hamilton
(I didn't downvote it). I actually pointed out most of your last paragraph in this comment, but deleted it. I agree with you. The only reason I can even think to downvote your response would be to suggest placing more emphasis on 'don't do this unless you have no other choice.' I personally find downvotes with no explanation rude, so I'm going to upvote you (as your info was spot on). I do agree - the problem, as described, is a symptom of a messy interface/inheritance hierarchy.
kyoryu
Also, +1 for pointing out explicit interface implementation, which is the *minimum* you should do in this scenario. You have to implement the method, but at least you can hide it as much as possible!
kyoryu
Thanks kyoryu. Explicit implementation is a feature I only really discovered when trying to work out why ReadOnlyCollection didn't have an Insert method but still implemented IList! :)
Matt Hamilton
@Matt Hamilton: Amusingly enough, I've done the exact same thing with creating ReadOnlyCollection (ReadOnlyObservableCollection, specifically) lookalikes.
kyoryu
A: 

A few thoughts:

  1. Interface Separation Principle. Interfaces should be as small as possible, and only contain things that cannot be separated. Since MakePlayfulMeow() and MakeHiss() are not intrinsically tied together, they should be on two separate interfaces.

  2. You're running into a common problem with deep inheritance trees, especially of the type of inheritance that you're describing. Namely, there's commonly three objects that have three different behaviors in common, only none of them share the same set. So a Lion might Lick() and Roar(), a Cheetah might Meow() and Lick(), and an AlienCat might Roar() and Meow(). In this scenario, there's no clear inheritance hierarchy that makes sense. Because of situations like these, it often makes more sense to separate the behaviors into separate classes, and then create aggregates that combine the appropriate behaviors.

  3. Consider whether that's the right design anyway. You normally don't tell a cat to purr, you do something to it that causes it to purr. So instead of MakePlayfulMeow() as a method on the cat, maybe it makes more sense to have a Show(Thing) method on the cat, and if the cat sees a Toy object, it can decide to emit an appropriate sound. In other words, instead of thinking of your program as manipulating objects, think of your program as a series of interactions between objects. In this type of design, interfaces often end up looking less like 'things that can be manipulated' and more like 'messages that an object can send'.

  4. Consider something closer to a data-driven, discoverable approach rather than a more static approach. Instead of Cat.MakePlayfulMeow(), it might make more sense to have something like Cat.PerformAction(new PlayfulMeowAction()). This gives an easy way of having a more generic interface, which can still be discoverable (Cat.GetPossibleActions()), and helps solve some of the 'Lions can't purr' issues common in deep inheritance hierarchies.

  5. Another way of looking at things is to not make interfaces necessarily match class definitions 1:1. Consider a class to define what something is, and an interface as something to describe its capabilities. So whether FriendlyCat should inherit from something is a reasonable question, but the interfaces it exposes should be a description of its capabilities. This is slightly different, but not totally incompatible, from the idea of 'interfaces as message declarations' that I suggested in the third point.

kyoryu
Hi Kyoryu. Thanks for your feedback, much appreciated. I think your feedback makes good sense for the scenario I provided (indeed, a 'meow' is more of a reaction vs. a command / response). But, I was using the cat scenario to help me understand some more basic concepts and perhaps I didn't chose the best example. I think I have a handle on #1 now, but still need clarification on #2 per my edit. It may be basic but I am just learning. Thanks much for your input.
dushkin
@dushkin: In my opinion, an interface should *not* be a reflection of all of the methods of a class. It should be a reflection of the messages that two classes use to communicate, so if a class has multiple 'logical' endpoints, it may well implement multiple interfaces, each of which is designed to be used by a different consumer. While it's possible that a class ends up that way, care should be taken in interface design to ensure that each interface is *atomic* and binds together only those things that should truly be bound together.
kyoryu
@dushkin: If 'Purr' and 'Meow' don't always go together, they shouldn't be a single interface. If they happen to, that's great, make another interface that inherits both. But unless you can guarantee that this will be true for *all* cases, make sure you leave a way to opt out. So `Read()` and `Write()` aren't necessarily tied together, as something may be read-only. However, for some cases at least, `Add()` and `Delete()` probably are.
kyoryu
@dushkin: Now, in many cases, you'll want an interface that has all four of those methods - fine, compose it of the smaller interfaces. In general, you'll probably find things a lot easier if you think of class/interface design not in terms of 'how much can I stick together' but in terms of 'what is it impossible to pull apart?' Also, if your interfaces are designed by the consumers of the interface, rather than the consumer, they act well as a contract of what the implementor *must* support. The consumer of an object doesn't need an interface to do that - they can see the object already!
kyoryu
+1  A: 

Imagine that you could "pick and choose." For example, suppose you were allowed to not implement ICatSounds.MakeHiss() on FriendlyCat. Now what happens when a user of your classes writes the following code?

public ICatSounds GetCat()
{
  return new FriendlyCat();
}

ICatSounds cat = GetCat();
cat.MakeHiss();

The compiler has to let this pass: after all, GetCat is returning an ICatSounds, it's being assigned to an ICatSounds variable and ICatSounds has a MakeHiss method. But what happens when the code runs? .NET finds itself calling a method that doesn't exist.

This would be bad if it were allowed to happen. So the compiler requires you to implement all the methods in the interface. Your implementation is allowed to throw exceptions, such as NotImplementedException or NotSupportedException, if you want to: but the methods have to exist; the runtime has to be able to at least call them, even if they blow up.

See also Liskov Substitution Principle. Basically, the idea is that if FriendlyCat is an ICatSounds, it has to be substitutable anywhere an ICatSounds is used. A FriendlyCat without a MakeHiss method is not substitutable because users of ICatSounds could use the MakeHiss method but users of FriendlyCat couldn't.

itowlson