8

I've been coding for about 8 years, however I still find inheritance is too flexible and sometimes it makes you totally confused with the code you have written. One simplest example would be:

abstract class AClass {
    protected void method1() {
        if(check()) {
            do1();
        } else {
            do2();
        }
    }
    protected abstract void do1();
    protected abstract void do2();
}

The intention of the class is that people can implement do1() and do2() so that some further logic can be done, however sometimes people decide to overload method1(), and then things become complicated immediately.

I find only in strategy pattern, code is reused well through inheritance, in most case the designer of the base class know its subclasses very well, and that inheritance is totally optional.

I have a class that's inherited by 4 classes - an IoHandler, and it's subclasses for server side, client side, edge server, origin server, and it begins to drive me crazy. I was always in code refactoring, I always came out with ideas I think would work and then were proven not. It's said that human brain can only hold 7 pieces of information one time, am I carrying too many?

tactoth
  • 543
  • 1
  • 3
  • 12
  • 1
    You know you can prevent overloading of specific methods? Mark it as `final` [if you're using java]. This effectively makes it non-virtual, and it cannot be overloaded. Perfect for use in this circumstance [template pattern] – Farrell May 26 '11 at 12:40
  • @Farrell In fact I mark it 'protected intensively so that it can be overloaded'. I think when I do this I'm using some ideas from MFC, which was badly designed. – tactoth May 27 '11 at 02:42
  • 2
    Method overloading has nothing to do with inheritance. It's really weird that several comments and even the accepted answer repeat it verbatim. Surely you are talking about *overriding*? – Aaronaught Jun 23 '11 at 18:38
  • @Aaronaught Well your correction is really useful :( – tactoth Jun 27 '11 at 08:59

6 Answers6

19

Yes, it can be very tricky ... and you are in good company.

Joshua Bloch in Effective Java nicely captures some of the issues of inheritance and recommends to favor composition instead of inheritance.

Inheritance is a powerful way to achieve code reuse, but it is not always the best tool for the job. Used inappropriately, it leads to fragile software. It is safe to use inheritance within a package, where the subclass and the superclass implementation are under the control of the same programmers. It is also safe to use inheritance when extending classes specifically designed and documented for extension (Item 15). Inheriting from ordinary concrete classes across package boundaries, however, is dangerous. As a reminder, this book uses the word “inheritance” to mean implementation inheritance (when one class extends another). The problems discussed in this item do not apply to interface inheritance (when a class implements an interface or where one interface extends another).

Unlike method invocation, inheritance breaks encapsulation. In other words, a subclass depends on the implementation details of its superclass for its proper function. The superclass's implementation may change from release to release, and if it does, the subclass may break, even though its code has not been touched. As a consequence, a subclass must evolve in tandem with its superclass, unless the superclass's authors have designed and documented it specifically for the purpose of being extended.

Dakotah North
  • 3,353
  • 1
  • 20
  • 31
  • +1. And note that the idea has been around a lot longer and is a lot more widespread than just Block and _Effective Java_; see [here](http://programmers.stackexchange.com/q/65179/3223). – Josh Kelley Feb 21 '13 at 15:06
3

Once you keep repeating such patterns, they don't seem so tricky anymore.

Also, you have to know about base classes before inheriting off them. And inheritance won't be optional as long as common code gets reused. Take any other pattern like abstract factory/composite - that inheritance actually serves a purpose.

Rule of the thumb: Don't use inheritance just to hold a bunch of classes together. Use it wherever it makes sense. In the contrived example, the user of the child class that is overloading the method1 must have some serious reason to do so. (OTOH, I have also wanted a way to prevent overloading certain functions alone - to avoid this). And before using that child class, you should know about this quirk as well. But a pure strategy pattern won't do it - if it does, doc it very well.

If you give your actual code example, may be folks here can review it for you.

Jeroen De Dauw
  • 410
  • 5
  • 15
  • Thanks for the answer, I still think inheritance is mainly used to share logic, I've always been applying the principle that when codes can be shared, they should be shared, and when you decide not to share them, you find it becomes clearer. – tactoth May 26 '11 at 08:10
  • The code example is too big, I have 30+ classes needs to be sorted out. – tactoth May 26 '11 at 08:11
1

I think your question is too vague and general. What you describe sounds like a complex problem, with or without inheritance. So IMHO it is entirely normal that you are struggling with it.

Of course in some cases inheritance is not the best tool for a job (obligatory reference to "favor composition over inheritance" ;-). But when used with care, I find it useful. From your description it is hard to decide whether or not inheritance is the right tool for your case.

Péter Török
  • 46,427
  • 16
  • 160
  • 185
  • It's a vague question and the intention is to collect some ideas to make it easy to make design decisions. I've been switching between composition and inheritance back and forth. – tactoth May 26 '11 at 08:14
1

if your methods are overly complex, it makes subclasses cry

make your methods do one specific thing

Steven A. Lowe
  • 33,808
  • 2
  • 84
  • 151
1

Composition inheritance narrows the vector of change.

Imagine your solution is implemented and you use AClass in 300 different places of the code base, and now there is a requirement to change 45 of the method1() calls so that a do3() method is called instead of do2().

You could implement do3() on the IAnInterface (hopefully all implementations of the interface can handle a do3() method easily) and create a BClass that calls do1() or do3() in the method1() call and change the 45 references to both the AClass and the injected dependency.

now image implementing this same change for do4(), do5(), ...

Or you could do the following...

interface IFactoryCriteria {
    protected boolean check();
}

public final class DoerFactory() {
    protected final IAnInterfaceThatDoes createDoer( final IFactoryCriteria criteria ) {
        return criteria.check() ? new Doer1() : new Doer2();
    }
}

interface IAnInterfaceThatDoes {
    abstract void do();
}

public final class AClass implements IFactoryCriteria {
    private DoerFactory factory;
    public AClass(DoerFactory factory)
    {
        this.factory = factory;
    }

    protected void method1() {
        this.factory.createDoer( this ).do();
    }

    protected boolean check() {
        return true;//or false
    }
}

Now you just need to implement a second factory that returns either Doer1 or Doer2 and change the factory injection in 45 places. AClass doesn't change, the IAnInterface doesn't change, and you can easily handle more changes like do4(), do5().

Heath Lilley
  • 1,290
  • 9
  • 12
  • 1
    +1 I like this implemementation of the problem over that proposed by Ed. I think this makes the issue more SRP as well as limiting the dependencies AClass has. I guess my main question would be the pros and cons of just passing IAnInterfaceThatDoes into AClass instead of a factory class. For example what happens if the logic to determine the right IAnInterfaceThatDoes falls outside the scope of the factory? – dreza Jun 23 '11 at 20:40
  • I made the factory the dependency just to minimize IAnInterfaceThatDoes's exposure. If the calling code needed the access to that interface then you could certainly move it out. I don't know of a situation where I would want to move the interface creation out of the factory. I would say that if that has to change then implementing a DoerFactoryI interface with the createDoer method exposed would be another way to go. You could then implement a factory using a DI framework or something. – Heath Lilley Jun 24 '11 at 12:53
0

Why not do:

interface IAnInterface {
    abstract void do1();
    abstract void do2();
}

public final class AClass {
    private IAnInterface Dependent;
    public AClass(IAnInterface dependent)
    {
        this.Dependent = dependent;
    }

    protected void method1() {
        if(check()) {
            this.Dependent.do1();
        } else {
            this.Dependent.do2();
        }
    }
}

(I think that the final is the equivalent of "sealed" in C#, someone please correct me if that syntax is incorrect).

Dependency injection/IoC means you can inherit only the methods you actually want people to be able to change, which I think solves your issue. Plus, it doesn't sound like the main class actually does whatever do1 and do2 are, more that it delegates the calls into its subclasses based on a condition: you're breaking separation of concerns!

Ed James
  • 3,489
  • 3
  • 22
  • 33