When to use inheritance?

2019-02-18 06:49发布

问题:

Me and my friend had a little debate. I had to implement a "browser process watcher" class which invokes an event whenever the browser that is being watched (let's say Internet explorer) is running.

We created a "Process watcher" class, and here starts the debate:

He said that the constructor should only accept strings (like "iexplore.exe"), and i said we should inherit "Process watcher" to create a "browser watcher" which accepts the currently used browser enum, which the constructor will "translate" it to "iexplore". he said we should use a util function which will act as the translator.

I know both ways are valid and good, but i wonder whats the pros and cons of each, and what is suitable in our case.

回答1:

Lately I've been taking the approach of "Keep it simple now, and refactor later if you need to extend it".

What you're doing right now seems pretty simple. You only really have one case that you're trying to handle. So I'd say take the simpler approach for now. In the end, if you never have to make another kind of watcher then you'll avoid the extra complexity. However, code it in a way that will make it easier to refactor later if you need to.

In the future, if you find you need another type of watcher, spend the effort then to refactor it into an inheritance (or composition, or whatever other pattern you want to follow). If your initial code is done right the refactoring should be fairly easy, so you're not really adding much extra work.

I've found this approach works fairly well for me. In the cases where I really didn't need inheritance the code stays simple. But when I really do need it I can add it in without any real problems.



回答2:

Other things being equal I prefer the simpler solution (a single concrete class which takes a string as a constructor parameter) to the more complicated one (using a base class and a subclass).

Inheritance is appropriate when you want to vary behaviour: if the browser watcher will do something that the ordinary process watcher doesn't. But if you only want to vary the value of the data, then just vary the data.



回答3:

If you have no other use for ProcessWatcher than to serve as the parent of BrowserWatcher than you shouldn't create it. If other Watchers are being implemented that have shared functionality that can be placed in ProcessWatcher, then you should (both are "isa" relationships so Rob's criterion is met).

It really is as simple as that. Arguing that some day you'll have other watchers is not an argument in favor of creating a separate class. It is a mental tic that you should lose ASAP.



回答4:

Inheritance should only ever be used to implement an "isa" relationship.

As you can say that a "browser watcher" is a specific instance of a "process watcher" then inheritance is suitable for this architecture.

Hence, for me, having the identity of what you are watching passed through as a part of the construction of the browser watcher implementation of the "process watcher" is definitely the way to go.

Edit: More specifically, inheritance is for when you want to specialise behaviour. For example, most animals make a sound, but you could hope to provide which sound to make in a class called animal, you must wait for the specialisation.

So then we have Horse class providing a "neigh" for its sound, a Dog class providing a "bark" for its sound, etc.

HTH

cheers,

Rob



回答5:

Depends on what use case you have or what god you follow.

I don't say "inheritance is evil" but generally I follow the principle "Favor composition over inheritance" to avoid excessive class hierarchies.



回答6:

I agree that in most cases, simplicity over complexity is a good strategy, as long as your simplicity is not too short-sighted (ref. Herms, write code in such a way that you can easily re-factor later).

However, I also know how difficult it can be to shut up that bug in your ear that encourages a more thorough design. If you still want to favor inheritance without necessarily thinking in terms of "base class" and "subclass", you can simply define an interface (ex. IProcessWatcher) which is implemented by ProcessWatcher. When you use the ProcessWatcher object, refer to it in terms of the interface so that if you later decide to create a BrowserWatcher (or any other kind of ProcessWatcher), you can do so without forcing it to descend from ProcessWatcher, as long as it implements the IProcessWatcher interface.

Warning: Proceed with caution. It gets tempting to want to define an interface for every single object, and let's face it, that just ridiculous. =)

Ultimately, you need to find something that you're both comfortable with, since you will both have to maintain this code, and I think this might be a nice compromise, rather than simply "Inheritance or No inheritance".

Good luck!