I’m having a problem in relation to using super and overriding. Basically, class B which extends A has a setter for the current state of the class. Inside the setter, depending on the value of the current state, a different event can be executed.
In the setter for B the first thing that happens is that it called super so that the setter for A can go launch general events. Then the control returns to the setter of B where I can execute specific events if needed.
The problem comes when A executes events that call the setter and so it can go multiple depths before returning back to to B.
The following code illustrates what I’m talking about (it’s groovy, but that’s irrelevant):
class A
{
public int num = 0;
public void setFoo( int i )
{
println "A: $i";
num = i + 1;
// what's actually happening, is that in the setter, depending
// on the value, an event can be executed, which will in turn
// call setFoo() on the class. this is just the equivalet
if( i < 3 )
this.setFoo( num );
}
}
class B extends A
{
public void setFoo( int i )
{
println "B: $i - num $num";
super.setFoo( i );
println "After super for $i - num: $num";
}
}
def B = new B();
B.foo = 0;
This results in an output of:
B: 0 - num 0
A: 0
B: 1 - num 1
A: 1
B: 2 - num 2
A: 2
B: 3 - num 3
A: 3
After super for 3 - num: 4
After super for 2 - num: 4
After super for 1 - num: 4
After super for 0 - num: 4
When I come back to B after the call to super (“After super for…”) the value of num is always the same, meaning that it screws with what I’m trying to do in B (i.e. launch specific events).
Some points on the architecture to begin with:
- “Why not use
iinstead ofnumin the setter forB“? – This is just the easiest example to show the problem – what’s actually happening in my code is different, just the same problem. In my case, I have access tonum, noti. Even if I rewrote part of it to passi, the state of the class will have moved on (due to the base class) - It’s a server environment, so I don’t have access to a frame loop or something similar. It’s event based.
- It should be possible to async execute the event, or set the event up to schedule later, but that requires a lot of advance knowledge of where and when the event is going to be used, which breaks the whole point of events in the first place
What I’m looking for is a way to launch events based on the state of the class, but have it happen after the return from super (while still working for the base class) if that makes any sense.
Ideas?
EDIT
To give a better idea of the code I’m using (based on Don’s suggestion to use a callback), here is a simplified version of what I have. (If you want to run it, you can just copy it into http://groovyconsole.appspot.com/):
// A is our base class
class A{
public int currentState= 0;
public void setCurrentState( int i )
{
this.currentState = i;
this._onStateChanged();
}
protected void _onStateChanged()
{
println "The state in A is $currentState";
// depending on the state launch some events.
// these can changed the current state of
// B
if( this.currentState == 0 )
{
def event = new MyEvent( this );
event.execute();
}
}
}
// B is a more specific version of A
class B extends A
{
protected void _onStateChanged()
{
println "The state in B is $currentState";
super._onStateChanged();
println "The state in B afterwards is $currentState";
// launch specific events based on the current state
if( this.currentState == 0 )
println "Launch a specific event!";
}
}
// simple event class that can change the status of B
class MyEvent
{
private B b = null;
public MyEvent( B b )
{
this.b = b;
}
public void execute()
{
// do some stuff
b.currentState++;
}
}
// program start
def b = new B();
b.currentState = 0;
B has to call super as there are some states where I want a basic plus a specific event. Basic events are normally used to set the program state, while specific ones are there to react.
In this example, my output is:
The state in B is 0
The state in A is 0
The state in B is 1
The state in A is 1
The state in B afterwards is 1
The state in B afterwards is 1
i.e. B never gets to react to the state being 0
Edit
If I change the super() call in B to the end of _onStateChanged() rather than the start, this will give the chance for it to react to the state before it gets changed. Is this a simple solution to this problem, or just wrong?
Edit
So I came up with this (again, you can copy it into the groovy console appspot site):
// A is our base class
class A{
public int currentState = 0;
public int nextState = 0;
public boolean canChange = true;
public void setCurrentState( int i )
{
if( this.canChange )
{
this.currentState = i;
this._onStateChanged();
}
else
this.nextState = i;
}
protected void _onStateChanged()
{
println "The state in A is $currentState";
// depending on the state launch some events.
// these can changed the current state of
// B
if( this.currentState == 0 )
{
def event = new MyEvent( this );
event.execute();
}
}
}
// B is a more specific version of A
class B extends A
{
protected void _onStateChanged()
{
this.canChange = false;
println "The state in B is $currentState";
super._onStateChanged();
println "The state in B afterwards is $currentState";
// launch specific events based on the current state
if( this.currentState == 0 )
println "Launch a specific event!";
this.canChange = true;
if( this.nextState != 0 )
{
int state = this.nextState;
this.nextState = 0;
this.currentState = state;
}
}
}
// simple event class that can change the status of B
class MyEvent
{
private B b = null;
public MyEvent( B b )
{
this.b = b;
}
public void execute()
{
// do some stuff
b.currentState++;
}
}
// program start
def b = new B();
b.currentState = 0;
It gives me the desired output:
The state in B is 0
The state in A is 0
The state in B afterwards is 0
Launch a specific event!
The state in B is 1
The state in A is 1
The state in B afterwards is 1
but is kind of ugly. Better way?
Ok, so I have two solutions to this. This first one is the last code sample provided. It adds another parameters to check if we can change, and if so, does, otherwise it waits:
The second solution takes a more listener like approach. Both the base and the extending class register functions to call when the state changes:
Both give me the output I’m looking for, though the second one is slightly broken, as it breaks the normal convention of adding listeners. Listeners are added to the start of the list, rather than the end. That’ll give me an output like:
So B gets called first, but at least they’re in a good order. If I just push listeners to the list, I get:
So B will get to react to the state being 0, but the order is reversed and by that stage, the state has changed to something else. It also breaks a bit in that it requires knowledge that B will never launch an event that will change the state, as otherwise we still have the same problem.
Out of the two, I think the first one is the best (assuming a non-rewrite of the architecture/problem). It’s a bit more complicated, but doesn’t require prior knowledge anywhere and the events get called in the right order.
Unless someone can suggest a better architecture for the problem, I’ll go with that.