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
indraw()
ofSub11
but I think it violatesDRY
- 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
inSub1
andSub11
has the same reason to be calculated- I can create a
composite
instead of extendingSub1
inSub11
, but before that, I have to extendBase
in another class (Sub2
) and then useSub1
andSub2
in composite. Is it a good practice? - It is not possible to calculate
top
out ofdraw()
, because it's calculating needs to input arguments ofdraw()
anddraw()
may call multiple times with different arguments.
3 Answers 3
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
}
}
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– hasanghaforian2018年10月28日 06:56:10 +00:00Commented Oct 28, 2018 at 6:56
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
}
}
draw
return atop
value?Base
is a part of an API which is not designed by me.