开发者

Why use inheritance at all? [closed]

开发者 https://www.devze.com 2023-01-09 09:13 出处:网络
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references,or expertise, but this question will likely solicit debate, a
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance. Closed 10 years ago.

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).


[note: This question was originally tagged as being language-agnostic. Based on that, this answer was written to be fairly language agnostic, so it discusses inheritance as it's used across a wide range of languages, such as Smalltalk, C++, and Object Pascal. It's since been re-tagged as being specifically about Java. Java is different in defining a class and an interface as two entirely separate things. The idea that the purpose of inheritance is code reuse, not polymorphism is reasonable from a Java-specific viewpoint, but clearly wrong from a language-agnostic viewpoint. If you only care about Java, this probably isn't the best answer.]

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.


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.


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++.


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.


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.


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.


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


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.


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.


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.


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.


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


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.
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号