Reputation: 33
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...
Upvotes: 3
Views: 308
Reputation: 13065
A few thoughts:
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.
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.
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'.
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 'Lion
s can't purr' issues common in deep inheritance hierarchies.
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.
Upvotes: 0
Reputation: 74842
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.
Upvotes: 1
Reputation: 16505
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.
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.
Upvotes: 1
Reputation: 204259
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).
Upvotes: 2
Reputation: 34218
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.
Upvotes: 2