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:53

When I first started learning about object-oriented programming, I made the easy and probably common mistake of using inheritance to share common behavior - even where that behavior was not essential to the nature of the object.

To further build on an example much used in this particular question, there are lots of things that are petable - girlfriends, cars, fuzzy blankets... - so I might have had a Petable class that provided this common behavior, and various classes inheriting from it.

However, being petable is not part of the nature of any of these objects. There are vastly more important concepts that are essential to their nature - the girlfriend is a person, the car is a land vehicle, the cat is a mammal...

Behaviors should be assigned first to interfaces (including the default interface of the class), and promoted to a base class only if they are (a) common to a large group of classes that are subsets of a larger class - in the same sense that "cat" and "person" are subsets of "mammal".

The catch is, after you understand object-oriented design sufficiently better than I did at first, you'll normally do this automatically without even thinking about it. So the bare truth of the statement "code to an interface, not an abstract class" becomes so obvious you have a hard time believing anyone would bother to say it - and start trying to read other meanings into it.

Another thing I'd add is that if a class is purely abstract - with no non-abstract, non-inherited members or methods exposed to child, parent, or client - then why is it a class? It could be replaced, in some cases by an interface and in other cases by Null.

查看更多
查无此人
3楼-- · 2018-12-31 01:54

I recommend using composition instead of inheritence whenever possible. Use interfaces but use member objects for base implementation. That way, you can define a factory that constructs your objects to behave in a certain way. If you want to change the behavior then you make a new factory method (or abstract factory) that creates different types of sub-objects.

In some cases, you may find that your primary objects don't need interfaces at all, if all of the mutable behavior is defined in helper objects.

So instead of IPet or PetBase, you might end up with a Pet which has an IFurBehavior parameter. The IFurBehavior parameter is set by the CreateDog() method of the PetFactory. It is this parameter which is called for the shed() method.

If you do this you'll find your code is much more flexible and most of your simple objects deal with very basic system-wide behaviors.

I recommend this pattern even in multiple-inheritence languages.

查看更多
ら面具成の殇う
4楼-- · 2018-12-31 01:54

I have a rough rule-of-thumb

Functionality: likely to be different in all parts: Interface.

Data, and functionality, parts will be mostly the same, parts different: abstract class.

Data, and functionality, actually working, if extended only with slight changes: ordinary (concrete) class

Data and functionality, no changes planned: ordinary (concrete) class with final modifier.

Data, and maybe functionality: read-only: enum members.

This is very rough and ready and not at all strictly defined, but there is a spectrum from interfaces where everything is intended to be changed to enums where everything is fixed a bit like a read-only file.

查看更多
裙下三千臣
5楼-- · 2018-12-31 01:56

Modern style is to define IPet and PetBase.

The advantage of the interface is that other code can use it without any ties whatsoever to other executable code. Completely "clean." Also interfaces can be mixed.

But base classes are useful for simple implementations and common utilities. So provide an abstract base class as well to save time and code.

查看更多
与君花间醉酒
6楼-- · 2018-12-31 01:56

It depends on your requirements. If IPet is simple enough, I would prefer to implement that. Otherwise, if PetBase implements a ton of functionality you don't want to duplicate, then have at it.

The downside to implementing a base class is the requirement to override (or new) existing methods. This makes them virtual methods which means you have to be careful about how you use the object instance.

Lastly, the single inheritance of .NET kills me. A naive example: Say you're making a user control, so you inherit UserControl. But, now you're locked out of also inheriting PetBase. This forces you to reorganize, such as to make a PetBase class member, instead.

查看更多
刘海飞了
7楼-- · 2018-12-31 01:56

Explained well in this Java World article

Personally I tend to use interfaces to define interfaces - i.e. parts of the system design that specify how something should be accessed.

It's not uncommon that I will have a class implementing 1 or more interfaces.

Abstract classes I use as a basis for something else.

The following is an extract from the above mentioned article JavaWorld.com article, author Tony Sintes, 04/20/01


Interface vs. abstract class

Choosing interfaces and abstract classes is not an either/or proposition. If you need to change your design, make it an interface. However, you may have abstract classes that provide some default behavior. Abstract classes are excellent candidates inside of application frameworks.

Abstract classes let you define some behaviors; they force your subclasses to provide others. For example, if you have an application framework, an abstract class may provide default services such as event and message handling. Those services allow your application to plug in to your application framework. However, there is some application-specific functionality that only your application can perform. Such functionality might include startup and shutdown tasks, which are often application-dependent. So instead of trying to define that behavior itself, the abstract base class can declare abstract shutdown and startup methods. The base class knows that it needs those methods, but an abstract class lets your class admit that it doesn't know how to perform those actions; it only knows that it must initiate the actions. When it is time to start up, the abstract class can call the startup method. When the base class calls this method, Java calls the method defined by the child class.

Many developers forget that a class that defines an abstract method can call that method as well. Abstract classes are an excellent way to create planned inheritance hierarchies. They're also a good choice for nonleaf classes in class hierarchies.

Class vs. interface

Some say you should define all classes in terms of interfaces, but I think recommendation seems a bit extreme. I use interfaces when I see that something in my design will change frequently.

For example, the Strategy pattern lets you swap new algorithms and processes into your program without altering the objects that use them. A media player might know how to play CDs, MP3s, and wav files. Of course, you don't want to hardcode those playback algorithms into the player; that will make it difficult to add a new format like AVI. Furthermore, your code will be littered with useless case statements. And to add insult to injury, you will need to update those case statements each time you add a new algorithm. All in all, this is not a very object-oriented way to program.

With the Strategy pattern, you can simply encapsulate the algorithm behind an object. If you do that, you can provide new media plug-ins at any time. Let's call the plug-in class MediaStrategy. That object would have one method: playStream(Stream s). So to add a new algorithm, we simply extend our algorithm class. Now, when the program encounters the new media type, it simply delegates the playing of the stream to our media strategy. Of course, you'll need some plumbing to properly instantiate the algorithm strategies you will need.

This is an excellent place to use an interface. We've used the Strategy pattern, which clearly indicates a place in the design that will change. Thus, you should define the strategy as an interface. You should generally favor interfaces over inheritance when you want an object to have a certain type; in this case, MediaStrategy. Relying on inheritance for type identity is dangerous; it locks you into a particular inheritance hierarchy. Java doesn't allow multiple inheritance, so you can't extend something that gives you a useful implementation or more type identity.

查看更多
登录 后发表回答