我在使用超级和覆盖方面遇到了问题。基本上,扩展B
的类A
具有该类当前状态的setter。在setter内部,根据当前状态的值,可以执行不同的事件。
在B
的setter中,首先发生的事情是调用super
,以便A
的setter可以启动一般事件。然后控件返回到B
的setter,我可以根据需要执行特定事件。
问题出现在A
执行调用setter的事件时,因此它可以在返回B
之前进入多个深度。
以下代码说明了我正在谈论的内容(它很时髦,但这无关紧要):
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;
这导致输出:
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
当我在调用B
(“After super for ...”)之后回到super
时,num
的值始终相同,这意味着它与我想在B
做什么(即发起特定事件)。
该架构的一些要点:
i
”的设置器中使用num
而不是B
? - 这只是展示问题的最简单的例子 - 我的代码中实际发生的事情是不同的,只是同样的问题。在我的情况下,我可以访问num
,而不是i
。即使我重写了部分内容以传递i
,类的状态也会继续(由于基类)我正在寻找的是一种基于类的状态启动事件的方法,但是在从super
返回之后(在仍然为基类工作的情况下)发生这种情况,如果这有任何意义的话
想法?
修改
为了更好地了解我正在使用的代码(根据Don的建议使用回调),这里是我所拥有的简化版本。 (如果要运行它,可以将其复制到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
必须致电super
,因为有些州我希望基本的加上特定事件。基本事件通常用于设置程序状态,而具体的事件可以作出反应。
在这个例子中,我的输出是:
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
即。 B永远不会对状态为0做出反应
修改
如果我将super()
中的B
调用更改为_onStateChanged()
的结尾而不是开头,则会有机会在状态发生变化之前对状态作出反应。这是解决这个问题的简单方法,还是错误的?
修改 所以我想出了这个(再次,你可以将它复制到groovy控制台appspot网站):
// 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;
它给了我想要的输出:
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
但有点难看。更好的方式?
答案 0 :(得分:2)
从根本上说,A.setFoo
已被打破
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 );
}
}
因为new A().setFoo(2)
(例如)会导致堆栈溢出。像下面这样的东西可能是更好的设计
abstract class A
{
public int num = 0;
abstract void setFooCallback(int i)
public final 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.setFooCallback( num );
}
}
class B extends A
{
public void setFooCallback( int i )
{
// Implement me to launch custom events or whatever
// If you call setFoo in here you'll get a stack overflow
}
}
如果您需要实例化A的实例,只需删除abstract
修饰符并将setFooCallback
更改为:
void setFooCallback(int i) { // default implementation does nothing }
仅供参考,我认为以上是模板(方法)模式的一个例子
答案 1 :(得分:0)
实际问题是Java会按值复制并传递所有参数。使用原语(如int i
),这会导致每个方法获得它自己的值副本。通常,这是为了防止你实际上要做的事情,因为方法副作用可能会让人感到惊讶
解决这个问题的快捷方法是返回修改后的i
值 - 因此,您的调用如下所示:i = super.foo(i);
。这有两个好处 - 1)它让人们知道您希望i
的值可能会发生变化,2)您不会依赖副作用来改变价值。
否则,您可以将i
更改为某种对象包装器(我犹豫地说Integer
,因为有一些封底优化的东西可能会弄乱这个设计)。但是如果你这样做,请记录wazoo中所需的行为,或者使用该方法的开发人员可能会对更改值感到惊讶(或者未来的维护者可能无法正确调整值)。
<小时/> 编辑: 好的,从它的声音来看,你想要对当前的和以前的状态作出反应,但是你永远不会存储以前的状态...
b._onStateChanged()
内的局部变量可能是你最好的选择。否则,您的程序将继续对当前状态做出反应(只是,当前状态不是您所期望的)
此外,您可能希望稍微更改一下您的体系结构 - 事实上,您永远不会知道您的默认行为(在A
内)是否仍然会被执行。再看看@ Don的推荐,因为我怀疑这将更符合您的架构需要的方向(您总是想要通用,对吗?)。换句话说,不要先调用特定行为 - 调用泛型,让它执行修改,并在完成后调用特定行为。如有必要,您还可以递归地调用setFoo()
方法,允许它为每个变异状态调用setFooCallback()
。
答案 2 :(得分:0)
我认为解决此问题的更好方法是使用Template Method设计模式。
答案 3 :(得分:0)
好的,所以我有两个解决方案。第一个是提供的最后一个代码示例。它添加了另一个参数来检查我们是否可以更改,如果是,则确实如此,否则它会等待:
// 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;
第二种解决方案采用更像听众的方法。基类和扩展类寄存器都可以在状态发生变化时调用:
// A is our base class
class A{
public int currentState = 0;
public def listeners = [];
public void setCurrentState( int i )
{
// call each of our listeners with the current state
this.currentState = i;
listeners.each { it( i ); }
}
public A()
{
this.addListener( this.&_onStateChanged );
}
public void addListener( def callback )
{
this.listeners.add( 0, callback );
}
protected void _onStateChanged( int state )
{
println "The state in A is $state";
// depending on the state launch some events.
// these can changed the current state of
// B
if( state == 0 || state == 1 )
{
def event = new MyEvent( this );
event.execute();
}
}
}
// B is a more specific version of A
class B extends A
{
public B()
{
super();
this.addListener( this.&_onBStateChanged );
}
protected void _onBStateChanged( int state )
{
println "The state in B is $state";
// launch specific events based on the current state
if( state == 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;
两者都给了我正在寻找的输出,虽然第二个稍微破坏了,因为它打破了添加监听器的常规惯例。监听器被添加到列表的开头,而不是结尾。这会给我一个输出:
The state in B is 0
Launch a specific event!
The state in A is 0
The state in B is 1
The state in A is 1
The state in B is 2
The state in A is 2
所以首先调用B,但至少它们处于良好状态。如果我只是将听众推到列表中,我会得到:
The state in A is 0
The state in A is 1
The state in A is 2
The state in B is 2
The state in B is 1
The state in B is 0
Launch a specific event!
因此B将对状态为0做出反应,但顺序是相反的,在那个阶段,状态已经变为其他状态。它还打破了一点,因为它需要知道B将永远不会发起会改变状态的事件,否则我们仍会遇到同样的问题。
在这两者中,我认为第一个是最好的(假设不重写架构/问题)。它有点复杂,但在任何地方都不需要先验知识,事件会以正确的顺序调用。
除非有人能为这个问题建议一个更好的架构,否则我会坚持下去。
答案 4 :(得分:0)
对我而言,您似乎试图在'默认'B
事件之前执行特定的A
事件。在这种情况下,对我来说最合适的方法是首先执行B
个事件,然后调用super
来触发A
个事件。
更新:
使用模板方法模式:
class A{
public int currentState= 0;
public void setCurrentState( int i )
{
this.currentState = i;
println "The state in A is $currentState";
// Maybe you need to split your MyEvent into two events.
// MyEventA - Does whatever required before executing special events.
def eventA = new MyEventA( this );
eventA.execute();
this._invokeSpecialEvents();
// depending on the state launch some events.
if( this.currentState == 0 )
{
// MyEventB - Does whatever required after executing special events (do actual currentState change).
def event = new MyEventB( this );
event.execute();
}
}
protected void _invokeSpecialEvents()
{
// You mentioned that cannot make A class abstract.
// This method is empty rather than abstract only for that reason.
}
}