Interface vs Base class

2018-12-31 01:31发布

When should I use an interface and when should I use a base class?

Should it always be an interface if I don't want to actually define a base implementation of the methods?

If I have a Dog and Cat class. Why would I want to implement IPet instead of PetBase? I can understand having interfaces for ISheds or IBarks (IMakesNoise?), because those can be placed on a pet by pet basis, but I don't understand which to use for a generic Pet.

30条回答
骚的不知所云
2楼-- · 2018-12-31 01:49

@Joel: Some languages (e.g., C++) allow multiple-inheritance.

查看更多
宁负流年不负卿
3楼-- · 2018-12-31 01:49

I've found that a pattern of Interface > Abstract > Concrete works in the following use-case:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

The abstract class defines default shared attributes of the concrete classes, yet enforces the interface. For example:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Now, since all mammals have hair and nipples (AFAIK, I'm not a zoologist), we can roll this into the abstract base class

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

And then the concrete classes merely define that they walk upright.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

This design is nice when there are lots of concrete classes, and you don't want to maintain boilerplate just to program to an interface. If new methods were added to the interface, it would break all of the resulting classes, so you are still getting the advantages of the interface approach.

In this case, the abstract could just as well be concrete; however, the abstract designation helps to emphasize that this pattern is being employed.

查看更多
忆尘夕之涩
4楼-- · 2018-12-31 01:49

In general, you should favor interfaces over abstract classes. One reason to use an abstract class is if you have common implementation among concrete classes. Of course, you should still declare an interface (IPet) and have an abstract class (PetBase) implement that interface.Using small, distinct interfaces, you can use multiples to further improve flexibility. Interfaces allow the maximum amount of flexibility and portability of types across boundaries. When passing references across boundaries, always pass the interface and not the concrete type. This allows the receiving end to determine concrete implementation and provides maximum flexibility. This is absolutely true when programming in a TDD/BDD fashion.

The Gang of Four stated in their book "Because inheritance exposes a subclass to details of its parent's implementation, it's often said that 'inheritance breaks encapsulation". I believe this to be true.

查看更多
琉璃瓶的回忆
5楼-- · 2018-12-31 01:50

Another option to keep in mind is using the "has-a" relationship, aka "is implemented in terms of" or "composition." Sometimes this is a cleaner, more flexible way to structure things than using "is-a" inheritance.

It may not make as much sense logically to say that Dog and Cat both "have" a Pet, but it avoids common multiple inheritance pitfalls:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Yes, this example shows that there is a lot of code duplication and lack of elegance involved in doing things this way. But one should also appreciate that this helps to keep Dog and Cat decoupled from the Pet class (in that Dog and Cat do not have access to the private members of Pet), and it leaves room for Dog and Cat to inherit from something else--possibly the Mammal class.

Composition is preferable when no private access is required and you don't need to refer to Dog and Cat using generic Pet references/pointers. Interfaces give you that generic reference capability and can help cut down on the verbosity of your code, but they can also obfuscate things when they are poorly organized. Inheritance is useful when you need private member access, and in using it you are committing yourself to highly coupling your Dog and Cat classes to your Pet class, which is a steep cost to pay.

Between inheritance, composition, and interfaces there is no one way that is always right, and it helps to consider how all three options can be used in harmony. Of the three, inheritance is typically the option that should be used the least often.

查看更多
永恒的永恒
6楼-- · 2018-12-31 01:50

Juan,

I like to think of interfaces as a way to characterize a class. A particular dog breed class, say a YorkshireTerrier, may be a descended of the parent dog class, but it is also implements IFurry, IStubby, and IYippieDog. So the class defines what the class is but the interface tells us things about it.

The advantage of this is it allows me to, for example, gather all the IYippieDog's and throw them into my Ocean collection. So now I can reach across a particular set of objects and find ones that meet the criteria I am looking at without inspecting the class too closely.

I find that interfaces really should define a sub-set of the public behavior of a class. If it defines all the public behavior for all the classes that implement then it usually does not need to exist. They do not tell me anything useful.

This thought though goes counter to the idea that every class should have an interface and you should code to the interface. That's fine, but you end up with a lot of one to one interfaces to classes and it makes things confusing. I understand that the idea is it does not really cost anything to do and now you can swap things in and out with ease. However, I find that I rarely do that. Most of the time I am just modifying the existing class in place and have the exact same issues I always did if the public interface of that class needs changing, except I now have to change it in two places.

So if you think like me you would definitely say that Cat and Dog are IPettable. It is a characterization that matches them both.

The other piece of this though is should they have the same base class? The question is do they need to be broadly treated as the same thing. Certainly they are both Animals, but does that fit how we are going to use them together.

Say I want to gather all Animal classes and put them in my Ark container.

Or do they need to be Mammals? Perhaps we need some kind of cross animal milking factory?

Do they even need to be linked together at all? Is it enough to just know they are both IPettable?

I often feel the desire to derive a whole class hierarchy when I really just need one class. I do it in anticipation someday I might need it and usually I never do. Even when I do, I usually find I have to do a lot to fix it. That’s because the first class I am creating is not the Dog, I am not that lucky, it is instead the Platypus. Now my entire class hierarchy is based on the bizarre case and I have a lot of wasted code.

You might also find at some point that not all Cats are IPettable (like that hairless one). Now you can move that Interface to all the derivative classes that fit. You will find that a much less breaking change that all of a sudden Cats are no longer derived from PettableBase.

查看更多
无与为乐者.
7楼-- · 2018-12-31 01:53

Prefer interfaces over abstract classes

Rationale, the main points to consider [two already mentioned here] are :

  • Interfaces are more flexible, because a class can implement multiple interfaces. Since Java does not have multiple inheritance, using abstract classes prevents your users from using any other class hierarchy. In general, prefer interfaces when there are no default implementations or state. Java collections offer good examples of this (Map, Set, etc.).
  • Abstract classes have the advantage of allowing better forward compatibility. Once clients use an interface, you cannot change it; if they use an abstract class, you can still add behavior without breaking existing code. If compatibility is a concern, consider using abstract classes.
  • Even if you do have default implementations or internal state, consider offering an interface and an abstract implementation of it. This will assist clients, but still allow them greater freedom if desired [1].
    Of course, the subject has been discussed at length elsewhere [2,3].

[1] It adds more code, of course, but if brevity is your primary concern, you probably should have avoided Java in the first place!

[2] Joshua Bloch, Effective Java, items 16-18.

[3] http://www.codeproject.com/KB/ar...

查看更多
登录 后发表回答