Why use inheritance at all? [closed]

六眼飞鱼酱① 提交于 2019-12-17 05:39:25

问题


I know the question has been discussed before, but it seems always under the assumption that inheritance is at least sometimes preferable to composition. I'd like to challenge that assumption in hopes of gaining some understanding.

My question is this: Since you can accomplish anything with object composition that you can with classical inheritance and since classical inheritance is very often abused[1] and since object composition gives you flexibility to change the delegate object runtime, why the would you ever use classical inheritance?

I can sort of understand why you would recommend inheritance in some languages like Java and C++ that do not offer convenient syntax for delegation. In these languages you can save a lot of typing by using inheritance whenever it is not clearly incorrect to do so. But other languages like Objective C and Ruby offer both classical inheritance and very convienient syntax for delegation. The Go programming language is the only langage that to my knowledge has decided that classical inheritance is more trouble than it's worth and supports only delegation for code reuse.

Another way to state my question is this: Even if you know that classical inheritance is not incorrect to implement a certain model, is that reason enough to use it instead of composition?

[1] Many people use classical inheritance to achieve polymorphism instead of letting their classes implement an interface. The purpose of inheritance is code reuse, not polymorphism. Furthermore, some people use inheritance to model their intuitive understanding of an "is-a" relationship which can often be problematic.

Update

I just want to clarify what I mean exactly when I talk about inheritance:

I am talking about the kind of inheritance whereby a class inherits from a partially or fully implemented base class. I am not talking about inheriting from a purely abstract base class which amounts to the same thing as implementing an interface, which I for the record am not arguing against.

Update 2

I understand that inheritance is the only way to achieve polymorphism i C++. In that case it's obvious why you must use it. So my question is limited to languages such as Java or Ruby that offer distinct ways to achieve polymorphism (interfaces and duck typing, respectively).


回答1:


If you delegate everything that you haven't explicitly overridden to some other object implementing the same interface (the "base" object), then you've basically Greenspunned inheritance on top of composition, but (in most languages) with a lot more verbosity and boilerplate. The purpose of using composition instead of inheritance is so that you can only delegate the behaviors you want to delegate.

If you want the object to use all the behavior of the base class unless explicitly overridden, then inheritance is the simplest, least verbose, most straightforward way to express it.




回答2:


The purpose of inheritance is code reuse, not polymorphism.

This is your fundamental mistake. Almost exactly the opposite is true. The primary purpose of (public) inheritance is modeling the relationships between the classes in question. Polymorphism is a large part of that.

When used correctly, inheritance isn't about reusing existing code. Rather, it's about being used by existing code. That is to say, if you have existing code that can work with the existing base class, when you derive a new class from that existing base class that other code can now automatically work with your new derived class as well.

It is possible to use inheritance for code re-use, but when/if you do so it should normally be private inheritance not public inheritance. If the language you're using supports delegation well, chances are pretty good that you rarely have much reason to use private inheritance. OTOH, private inheritance does support a few things that delegation (normally) doesn't. In particular, even though polymorphism is a decidedly secondary concern in this case, it can still be a concern -- i.e., with private inheritance you can start from a base class that's almost what you want, and (assuming it allows it) override the parts that aren't quite right.

With delegation your only real choice is to use the existing class exactly as it stands. If it doesn't do quite what you want, your only real choice is to ignore that functionality completely, and re-implement it from the ground up. In some cases that's no loss, but in others it's quite substantial. If other parts of the base class use the polymorphic function, private inheritance lets you override only the polymorphic function, and the other parts will use your overridden function. With delegation, you can't easily plug in your new functionality so other parts of the existing base class will use what you've overridden.




回答3:


The main reason for using inheritance is not as a form of composition - it is so you can get polymorphic behaviour. If you don't need polymorphism, you probably should not be using inheritance, at least in C++.




回答4:


Every one knows polymorphism is a great advantage of inheritance. Another Benefit which i find in inheritance is that helps to create replica of real world. For example in pay roll system we deal managers developers ,office boys etc. if we inherit all these class with super class Employee. it makes our program more understandable in context of real world that all these classes are basically employees. And one more thing classes not only contain methods they also contain attributes. So if we contain attributes generic to employee in the Employee class like social security number age etc. it would provide greater code reuse and conceptual clarity and of course polymorphism. However while using inheritance things we should keep in mind is the basic design principle "Identify the aspects of your application that vary and separate them from that aspects which change". You should never implement those aspect of application that change by inheritance instead use composition. And for those aspects which are not changeable u should use inheritance ofcourse if an obvious "is a" relation lies.




回答5:


Inheritance is to be preferred if:

  1. You need to expose the whole API of the class you extend (with delegation, you will need to write lots of delegating methods) and your language doesn't offer a simple way to say "delegate all unknown methods to".
  2. You need to access protected fields/methods for languages that have no concept of "friends"
  3. The advantages of delegation are somewhat reduced if your language allows multi-inheritance
  4. You usually have no need delegation at all if your language allows to dynamically inherit from a class or even an instance at runtime. You don't need it at all if you can control which methods are exposed (and how they are exposed) at the same time.

My conclusion: Delegation is a workaround for a bug in a programming language.




回答6:


I always think twice before using inheritance as it can get tricky fast. That being said there are many cases where it simply produces the most elegant code.




回答7:


Interfaces only define what an object can do and not how. So in simple terms interfaces are just contracts. All objects that implement the interface will have to define their own implementation of the contract. In practical world, this gives you separation of concern. Imagine yourself writing an application that needs to deal with various objects you don't know them in advance, still you need to deal with them, only thing you know is what all different things those objects are supposed to do. So you'll define an interface and mention all operations in the contract. Now you'll write your application against that interface. Later whoever wants to leverage your code or application will have to implement the interface on the object to make it work with your system. Your interface will force their object to define how each operation defined in the contract is supposed to be done. This way anyone can write objects that implement your interface, in order to have them flawlessly adapt to your system and all you know is what needs to be done and it is the object that needs to define how it is done.

In real-world development this practice is generally known as Programming to Interface and not to Implementation.

Interfaces are just contracts or signatures and they don't know anything about implementations.

Coding against interface means, the client code always holds an Interface object which is supplied by a factory. Any instance returned by the factory would be of type Interface which any factory candidate class must have implemented. This way the client program is not worried about implementation and the interface signature determines what all operations can be done. This can be used to change the behavior of a program at run-time. It also helps you to write far better programs from the maintenance point of view.

Here's a basic example for you.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

[STAThread]
static void Main()
{
    //This is your client code.
    ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
    speaker.Speak();
    Console.ReadLine();
}

public interface ISpeaker
{
    void Speak();
}

public class EnglishSpeaker : ISpeaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : ISpeaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak German.");
    }

    #endregion
}

public class SpanishSpeaker : ISpeaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    #endregion
}

alt text http://ruchitsurati.net/myfiles/interface.png




回答8:


What about the template method pattern? Let's say you have a base class with tons of points for customizable policies, but a strategy pattern doesn't make sense for at least one of the following reasons:

  1. The customizable policies need to know about the base class, can only be used with the base class and don't make sense in any other context. Using strategy instead is do-able but a PITA because both the base class and the policy class need to have references to each other.

  2. The policies are coupled to each other in that it wouldn't make sense to freely mix-and-match them. They only make sense in a very limited subset of all possible combinations.




回答9:


You wrote:

[1] Many people use classical inheritance to achieve polymorphism instead of letting their classes implement an interface. The purpose of inheritance is code reuse, not polymorphism. Furthermore, some people use inheritance to model their intuitive understanding of an "is-a" relationship which can often be problematic.

In most languages, the line between 'implementing an interface' and 'deriving a class from another' is very thin. In fact, in languages like C++, if you're deriving a class B from a class A, and A is a class which consists of only pure virtual methods, you are implementing an interface.

Inheritance is about interface reuse, not implementation reuse. It is not about code reuse, as you wrote above.

Inheritance, as you correctly point out, is meant to model an IS-A relationship (the fact that many people get this wrong has nothing to do with inheritance per se). You can also say 'BEHAVES-LIKE-A'. However, just because something has an IS-A relationship to something else doesn't meant that it uses the same (or even similiar) code to fulfill this relationship.

Compare this C++ example which implements different ways to output data; two classes use (public) inheritance so that they can be access polymorphically:

struct Output {
  virtual bool readyToWrite() const = 0;
  virtual void write(const char *data, size_t len) = 0;
};

struct NetworkOutput : public Output {
  NetworkOutput(const char *host, unsigned short port);

  bool readyToWrite();
  void write(const char *data, size_t len);
};

struct FileOutput : public Output {
  FileOutput(const char *fileName);

  bool readyToWrite();
  void write(const char *data, size_t len);
};

Now imagine if this was Java. 'Output' was no struct, but an 'interface'. It might be called 'Writeable'. Instead of 'public Output' you would say 'implements Writable'. What's the difference as far as the design is concerned?

None.




回答10:


Classical inheritance's main usefulness is if you have a number of related classes that will have identical logic for methods that operate on instance variables/properties.

There are really 3 ways to handle that:

  1. Inheritance.
  2. Duplicate the code (code smell "Duplicated code").
  3. Move the logic to yet another class (code smells "Lazy Class," "Middle Man," "Message Chains," and/or "Inappropriate Intimacy").

Now, there can be misuse of inheritance. For example, Java has the classes InputStream and OutputStream. Subclasses of these are used to read/write files, sockets, arrays, strings, and several are used to wrap other input/output streams. Based on what they do, these should have been interfaces rather than classes.




回答11:


One of the most useful ways I see to use inheritance is in GUI objects.




回答12:


When you asked:

Even if you know that classical inheritance is not incorrect to implement a certain model, is that reason enough to use it instead of composition?

The answer is no. If the model is incorrect (using inheritance), than it's wrong to use no matter what.

Here are some problems with inheritance that I've seen:

  1. Always having to test the run time type of derived class pointers to see if they can be cast up (or down too).
  2. This 'testing' can be achieved in various ways. You may have some sort of virtual method that returns a class identifier. Or failing that you may have to implement RTTI (Run time type identification) (At least in c/c++) which can give you a performance hit.
  3. class types that fail to get 'cast' up can be potentially problematic.
  4. There are many ways to cast your class type up and down the inheritance tree.



回答13:


Something totally not OOP but yet, composition usually means an extra cache-miss. It does depend but having the data closer is a plus.

Generally, I refuse to go into some religious fights, using your own judgment and style is the best you can get.



来源:https://stackoverflow.com/questions/3351666/why-use-inheritance-at-all

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!