Java super&;覆盖-更好的方法来做到这一点?

Java super&;覆盖-更好的方法来做到这一点?,java,architecture,groovy,Java,Architecture,Groovy,我在使用super和overriding时遇到了一个问题。基本上,扩展了A的类B具有类当前状态的setter。在setter内部,根据当前状态的值,可以执行不同的事件 在B的setter中,首先发生的事情是调用super,这样A的setter就可以启动常规事件。然后控件返回到B的setter,如果需要,我可以在那里执行特定事件 问题出现在A执行调用setter的事件时,因此它可以在返回B之前进入多个深度 下面的代码说明了我所说的内容(它是groovy,但与此无关): 调用super(“super

我在使用super和overriding时遇到了一个问题。基本上,扩展了
A
的类
B
具有类当前状态的setter。在setter内部,根据当前状态的值,可以执行不同的事件

B
的setter中,首先发生的事情是调用
super
,这样
A
的setter就可以启动常规事件。然后控件返回到
B
的setter,如果需要,我可以在那里执行特定事件

问题出现在
A
执行调用setter的事件时,因此它可以在返回
B
之前进入多个深度

下面的代码说明了我所说的内容(它是groovy,但与此无关):

调用
super
(“super for…”之后)返回
B
时,
num
的值总是相同的,这意味着它与我在
B
中尝试执行的操作(即启动特定事件)有关

关于体系结构的一些要点:

  • “为什么不在
    B
    的setter中使用
    i
    而不是
    num
    ”?-这只是说明问题的最简单的例子——我的代码中实际发生的是不同的,只是相同的问题。就我而言,我可以访问
    num
    ,而不是
    I
    。即使我重写了它的一部分以通过
    I
    ,类的状态也会继续(由于基类的原因)
  • 这是一个服务器环境,所以我无法访问帧循环或类似的内容。它是基于事件的
  • 应该可以异步执行事件,或者将事件设置为稍后的计划,但这需要提前了解事件将在何时何地使用,这首先会破坏整个事件点
我所寻找的是一种基于类的状态启动事件的方法,但是如果这有意义的话,在从
super
返回后(仍然为基类工作)启动事件

想法

编辑

为了更好地了解我正在使用的代码(基于Don关于使用回调的建议),这里是我所拥有的代码的简化版本。(如果要运行它,可以将其复制到):

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
i、 e.B永远不会对0的状态做出反应

编辑

如果我将
super()
调用
B
更改为
\u onStateChanged()
的结尾,而不是开始,这将使它有机会在更改前对状态做出反应。这是解决这个问题的简单方法,还是完全错误

编辑 所以我想到了这个(同样,您可以将它复制到groovy控制台appspot站点):

它为我提供了所需的输出:

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

但是有点难看。更好的方法?

从根本上说,
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 );
    }
}
如果需要实例化的实例,只需删除
abstract
修饰符并将
setFooCallback
更改为:

void setFooCallback(int i) { // default implementation does nothing }

仅供参考,我认为上面是模板(方法)模式的一个示例。实际问题是Java复制并按值传递所有参数。对于原语(如
inti
),这会导致每个方法获得自己的值副本。通常,这是为了防止您实际试图做的事情,否则方法的副作用可能会让人吃惊。
解决这个问题的一个简单快捷的方法是返回修改后的
i
——因此,您的调用如下所示:
i=super.foo(i)。这有两个好处-1)它让人们知道你期望
i
的值可能会改变,2)你不需要依靠副作用来改变值。
否则,您可以将
i
更改为某种对象包装器(我不太愿意说
Integer
,因为有一些隐藏的优化内容可能会把这个设计搞砸)。但是如果您这样做了,请将所需的行为记录到wazoo中,否则使用该方法的开发人员可能会对更改值感到惊讶(或者未来的维护人员可能无法正确调整值)


编辑: 好的,听上去,你想对当前状态和前一个状态做出反应,但是你从不存储前一个状态…
显然,您将不得不以某种方式存储上一个状态—在
b中包含一个局部变量。_onStateChanged()
可能是您最好的选择。否则,您的程序将继续对当前状态作出反应(只是,当前状态不是您所期望的)。

此外,您可能希望稍微改变一下您的体系结构—实际上,您永远不知道您的默认行为(在
a
中)是否仍将被执行。再次看看@Don的建议,因为我怀疑这将更符合您的体系结构需要的方向(您总是想要通用的,对吗?)。换句话说,不要先调用特定的行为——调用泛型,让它执行修改,然后让它在完成时调用特定的行为。如果有必要,您还可以让
setFoo()
方法递归地调用自身,允许它为每个变异状态调用
setFooCallback()

我认为更好的解决方法是使用设计模式。

好的,因此我有两种解决方案。第一个是提供的最后一个代码示例。它添加另一个参数以检查我们是否可以更改,如果可以,则会更改,否则将等待:

// 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;​
第二种解决方案采用了更像监听器的方法。两者都是b
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
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 );
    }
}
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
    }
}
void setFooCallback(int i) { // default implementation does nothing }
// 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
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!
​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.
}