AS3 / Flash:子组件调用父对象的方法是不是很糟糕?

时间:2010-02-04 20:18:31

标签: flash actionscript-3 oop

我在这里有一个非常简单的场景并且它有效,但我的直觉告诉我,我犯了一个错误,并希望通过一些更好的思想来运行它。

这里我有一个父类,它实例化一个MenuClass并处理页面之间的转换。

public class ParentClass extends Sprite
{
    public function ParentClass()
    {
        setupOptionMenu();
    }

    private function setupOptionMenu() : void
    {
        var myMenu:MenuClass = new MenuClass;
        myMenu.setUpButtons();
        this.addChild( myMenu );
    }

    + public function transitionForward() : void

    + public function transitionBackward() : void
}

这里是创建前进和后退按钮的MenuClass。单击每个将分别告诉上面的ParentClass transitionForward()或transitionBackward()。

public class MenuClass extends Sprite
{
    public function MenuClass()
    {
        setupButtons();
    }

    public function setUpButtons() : void
    {
        var backButton:Button = new Button();
        backButton.addEventListener( MouseEvent.CLICK, backClick );
        addChild( backButton );

        var forwardButton:Button = new Button();
        forwardButton.addEventListener( MouseEvent.CLICK, forwardClick );
        addChild( forwardButton );
    }

    private function backClick( e:Event ) : void
    {
        ParentClass( this.parent ).transitionForward();
    }

    private function forwardClick( e:Event ) : void
    {
        ParentClass( this.parent ).transitionBackward();
    }
}

一方面,我觉得MenuClass与其父级绑定得太紧,因此不太可重复使用。另一方面,我觉得,为了保持一切自包含,ParentClass不负责进入MenuClass实例并在那里添加事件监听器。这似乎应该有某种OOP经验法则来管理这种关系。

所以我的问题是:我的MenuClass是否与其父级太紧密了?如果是这样,你有什么建议可以放松这种耦合吗?

感谢。

4 个答案:

答案 0 :(得分:5)

如果您有任何机会想要提供多种导航方式(除了菜单之外),那么使用事件驱动方法是有道理的。它可以让你几乎完全将Parent与菜单分离。

这是我在Flash中实际测试过的一些代码。 ;)

父对象创建菜单对象并从中侦听FORWARD和BACKWARD事件:

package  
{
        import flash.display.Sprite;
        import flash.events.Event;

        public class ParentClass extends Sprite
        {
            public function ParentClass() 
            {
                setupOptionMenu();
            }

            private function setupOptionMenu() : void
            {
                var myMenu:MenuClass = new MenuClass;
                myMenu.setUpButtons();
                this.addChild( myMenu );

                myMenu.addEventListener(MenuClass.FORWARD, transitionForward);
                myMenu.addEventListener(MenuClass.BACKWARD, transitionBackward);
            }

            public function transitionForward(e:Event) : void
            { 
                trace("forward");
            }

            public function transitionBackward(e:Event) : void
            {
                trace("backward");
            }
        }
}

然后,当点击某些内容时,MenuClass会调度这些事件:

package  
{
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.MouseEvent;
        import flash.display.SimpleButton;

        public class MenuClass extends Sprite
        {
            public static const FORWARD:String = "forward";
            public static const BACKWARD:String = "backward";

            public function MenuClass() 
            {
                setUpButtons();
            }

            public function setUpButtons() : void
            {
                var rect:Sprite = new Sprite();
                rect.graphics.beginFill(0x666666);
                rect.graphics.drawRect(0,0,50,20);

                var backButton:SimpleButton = new SimpleButton(rect, rect, rect, rect);
                backButton.x = 10;
                backButton.y = 10;
                backButton.addEventListener( MouseEvent.CLICK, backClick );
                addChild( backButton );

                var forwardButton:SimpleButton = new SimpleButton(rect, rect, rect, rect);
                forwardButton.x = 70;
                forwardButton.y = 10;
                forwardButton.addEventListener( MouseEvent.CLICK, forwardClick );
                addChild( forwardButton );
            }

            private function backClick( e:MouseEvent ) : void
            {
                dispatchEvent(new Event(BACKWARD));
            }

            private function forwardClick( e:MouseEvent ) : void
            {
                dispatchEvent(new Event(FORWARD));
            }
        }
}

要提供备用UI方法,例如键盘快捷键或上下文菜单等,只需调度相同的FORWARD和BACKWARD事件,并让Parent也听取它们。您也可以创建一个新的Event子类,并在需要时将FORWARD和BACKWARD常量放在那里。

答案 1 :(得分:1)

  

另一方面,我觉得,为了保持一切自包含,ParentClass不负责进入MenuClass实例并在那里添加事件监听器。

为什么不呢?如果ParentClass需要监听MenuClass上的事件,那么它应该自己添加这些事件监听器。通过直接调用addEventListener,或者调用MenuClass上的另一个方法来设置具有正确函数的事件处理程序来调用。

答案 2 :(得分:0)

恕我直言,如果您的应用程序是小型/中型应用程序,您可以这样做,对于更大的项目,您正确地将父类和子类分离。 有几种基于品味的方法,一种是创建使用“FlowControl”类。

public class FlowControl
{
   private var _parentSprite : ParentClass;
   private var _instance : FlowControl;

   public static FlowControl GetInstance(ParentSprite : ParentClass)
   {

        if (_instance == null)
            _instance = new FlowControl(ParentSprite);
        return _instance;

   }

   private FlowControl(ParentSprite : ParentClass)
   {
       _parentSprite = ParentSprite;
   }

   public static void NextPage()
   {
       ParentSprite.transitionForward();
   }
}

我没有闪光灯来测试它,但你明白了。

更好的解决方案是实现一个接口,因此您可以在接口而不是类上获得紧密耦合。

答案 3 :(得分:0)

这不是“坏”的形式,但根据具体情况,可能还有其他方式来接近它。

特别是如果您有多个孩子只有一个父母。

我是静态方法和管理器类的忠实粉丝,但是有一大群人同样反对这种情况或者更糟糕,而不是与孩子 - >父方法调用相反。

值得注意的是,“parent”是一个取决于addChild()和显示列表的属性。

我可能错了,但我的理解是,如果您在代码中创建一个对象,并且它的生命周期中从未添加到显示列表中,那么parent将为null。所以这就提出了问题。

另一个怪癖是,如果你将一个子项添加到显示列表,然后删除它,父项仍然设置为它被添加到的最后一个对象,所以它可能能够对技术上的对象采取行动不再是孩子了。

这就是为什么我通常将这个逻辑说成如下

if(Type(this.parent).contains(this))
{
//do stuff
}

只有当对象出现在显示列表中时,包含才会返回true。

这种类型的逻辑可能相关的一些情况是,如果您在某些对象可能可见而其他对象不可见的对象数组上循环。在最坏的情况下,它会节省一些CPU周期。