0

I want to extend class Base which is defined in specific API and the extend the extended again. As you can see below, draw method of intermediate class calculates variable top and I need to use it in second subclass:

abstract class Base{
    public abstract void draw();
}

class Sub1 extends Base{
    @Override
    public void draw() {
        //calculate top
        int top = 10;
        //use "top"
    }
}

class Sub11 extends Sub1{
    @Override
    public void draw() {
        super.draw();
        //how to use "top" of "super.draw"?
    }
}

I think there are some options:

  • recalculating top in draw() of Sub11 but I think it violates DRY
  • extracting top as a field:

 

class Sub1 extends Base{

    private int top;

    public int getTop() {
        return top;
    }

    @Override
    public void draw() {
        int top = 10;
        this.top = top;
    }
}

class Sub11 extends Sub1{
    @Override
    public void draw() {
        super.draw();
        //use "top" of "super.draw"
        int top = getTop();
    }

    private void method(){
        //use "super.top" without executing "super.draw"
        int top = getTop();
    }
}

It seems that the second one is better, but it makes top accessible in other places of Sub11 without running super.draw() and this can cause bugs. Also commenting top with some things like Do not use before calling "draw()" cannot limit users from using that mistakenly before calling draw(). Is there a way to remove user's ability to do that wrong mistakenly?

Edit

  • top in Sub1 and Sub11 has the same reason to be calculated
  • I can create a composite instead of extending Sub1 in Sub11, but before that, I have to extend Base in another class (Sub2) and then use Sub1 and Sub2 in composite. Is it a good practice?
  • It is not possible to calculate top out of draw(), because it's calculating needs to input arguments of draw() and draw() may call multiple times with different arguments.
  • Why not have draw return a top value? – Erik Eidt Oct 27 '18 at 23:19
  • @ErikEidt As I mentioned, class Base is a part of an API which is not designed by me. – hasanghaforian Oct 28 '18 at 4:54
  • What about storing the top property as Multitons in a Flyweight? – kiltek Oct 30 '18 at 14:24
2

How about designing a more explicit contract for subclasses of Sub1?

abstract class Base {
    public abstract void draw();
}

class Sub1 extends Base {
    @Override
    public void draw() {
        //calculate top
        int top = 10;
        //use "top"

        drawWithTop(top);
    }

    protected void drawWithTop(int top) {}
}

class Sub11 extends Sub1 {
    @Override
    protected void drawWithTop(int top) {
        // you are in the "draw" context
        // use your top here
    }
}
2

I want to extend class Base which is defined in specific API and the extend the extended again.

Ugg, please don't do that. Long inheritance chains cause yo yo problems. Use inheritance if you have to to get into the API but once you're in don't keep using it over and over. Favor composition.

recalculating top in draw() of Sub11 but I think it violates DRY

Does Sub11 have a different reason to calculate top the way it does? DRY should be tempered with consideration of the Single Responsibility Principle. The modern definition of which is to be responsible to only one source of change. An older bit of wisdom along these lines comes from Matthew 6:24 "no one can serve two masters". Do not force top to be calculated in one place if there are two different reasons to calculate top certain ways that just happen to look identical at the moment. Not repeating yourself isn't about what you type with the keyboard. It's about what you mean.

extracting top as a field

..is a terrible idea if it means you have to write comments like

Do not use before calling "draw()"

The simple fix is to stop caching the calculation as a side effect of draw()

class Sub1 extends Base{
    @Override
    public void draw() {
        //calculate top somewhere else
        int top = calculateTop();
        //use "top"
    }

    public int calculateTop() {
        return 10; //If the calculation is expensive and needed often cache it here
    }
}
  • I edited my question, please see that again. – hasanghaforian Oct 28 '18 at 6:56
0

First of all, are you sure using double inheritance is the correct way to go? Generally, you are better off using composition instead:

abstract class Base {
    public abstract void draw();
}

class Sub1 extends Base {
    @Override
    public void draw() {
        // draw Sub1 stuff
    }
}

class Sub2 extends Base {

   private Base sub1 = new Sub1();

    @Override
    public void draw() {
        this.sub1.draw()
        // draw other sub2 stuff
    }
}

As for what you do with 'top', that depends on how exactly it is used and what it depends on. If it is something determined by the code using Sub1, such as something determining where to place it on a canvas or window, then it could be set using a constructor argument:

abstract class Base {
    public abstract void draw();
}

class Sub1 extends Base {

    private int top;

    public Sub1(int top) {
       this.top = top;
    }
    @Override
    public void draw() {
        // draw Sub1 stuff using top
    }
}

class Sub2 extends Base {

    private int top;
    private Base sub1;

    public Sub2() {
        this.top = 10; // compute top
        this.sub1 = new Sub1(this.top);
    }

    @Override
    public void draw() {
        this.sub1.draw()
        // draw other sub2 stuff
    }
}

If 'top' is something that is determined by Sub1 itself, like it's computed based on other properties of Sub1, then you could use a method to retrieve it. You could add a top method to Base, add it to Sub1, or create an interface/abstract subclass for things with a top. One of those probably makes the most sense given your situation.

Unless there are obviously other classes with a similar 'top' property that you plan on using, I would just create a method on Sub1 and not go overboard with creating a complicated type hierarchy. It would be trivial to add a HasTop interface later if it turns out you end up adding it as it wouldn't break any code already using Sub1.

abstract class Base {
    public abstract void draw();
}

class Sub1 extends Base {

    private int top = 10;

    @Override
    public void draw() {
        // draw Sub1 stuff using top
    }

    public void top() {
        return this.top;
    }
}

class Sub2 extends Base {

    private Sub1 sub1 = new Sub1();

    @Override
    public void draw() {
        this.sub1.draw()
        int top = this.sub1.top();
        // draw other sub2 stuff
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.