超级和超级的问题压倒一切 - 更好的方法吗?

时间:2011-07-11 13:04:57

标签: java architecture groovy

我在使用超级和覆盖方面遇到了问题。基本上,扩展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

但有点难看。更好的方式?

5 个答案:

答案 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.
}

}