Quantcast
Viewing latest article 5
Browse Latest Browse All 10

インターフェイスにクラスを継承させる

注意:IEventDispatcherなどすでにインターフェイスが用意されているものについてはもっとスマートな実装方法がある(最下部の追記参照)

***************

インターフェイスはクラスではないのでクラスは継承できない。インターフェイスに他クラスの機能を扱えるようにして、あたかもクラスを継承しているようにする方法。

イントロダクション

親をインターフェイスで定義しておいて、その子クラスを使う場合、プログラム上では直接子クラスへの参照を作るのではなく親インターフェイスへの参照で扱う方が「後々」都合がよい場合が多い。例えばIDoSomethingインターフェイスを継承したDoSomethingクラスを作る場合は以下のように記述してやる。

package {
    import flash.display.Sprite;
    
    //main
    public class main extends Sprite{
        //Defined as Interface
        private var _doSomething:IDoSomething;
        
        public function main(){
            //Creates a DoSomething class.
            _doSomething = new DoSomething();
            _doSomething.do();
        }
    }
}

package{
    public interface IDoSomething{
        function do():void;
    }
}

package{
    public class DoSomething implements IDoSomething{  
        public function do():void{
            //Something is done.
        }
    }
}

さてDoSomethingクラスは”クラス”なので、その他のクラスを継承することができる。例としてEventDispatcherクラスを継承させる。

package{
    import flash.events.EventDispatcher;
    
    public class DoSomething extends EventDispatcher implements IDoSomething{  
        public function do():void{
            //Do something
        }
    }
}

本題

これでDoSomethingクラスはイベントの送受信ができるようになった。ここでようやく本題(の問題)に突入。先ほどmainクラスではDoSomethingクラスを使う際、DoSomethingクラスそのものではなく親インターフェイス(IDoSomething)への参照を用いて使うことにしていた。IDoSomethingへの参照変数に参照されたDoSomethingクラスがイベントを送信したらどうなるか。IDoSomething.dispatchEvent()メソッドはインターフェイスには定義されていないのでコンパイルエラーになる。

package {
    import flash.display.Sprite;
    import flash.events.Event;
    
    //main
    public class main extends Sprite{
        //Defined as Interface
        private var _doSomething:IDoSomething;
        
        public function main(){
            _doSomething = new DoSomething();
            
            //Compile error as no such method is defined in the Interface.
            _doSomething.dispatchEvent(new Event("doSomething"));
        }
    }
}

package{
    public interface IDoSomething{
        function do():void;
    }
}

package{
    import flash.events.EventDispatcher;
    
    public class DoSomething extends EventDispatcher implements IDoSomething{  
        public function do():void{
            //Do something
        }
    }
}

解決策

DoSomethingクラスではたしかに実装されているメソッドなのに、IDoSomethingインターフェイスでは定義されていない。それにもかかわらずmainクラスではIDoSomethingインターフェイスとして扱いたい場合どうするか。「実装したいメソッドを扱えるクラスを戻り値とする」メソッドを新たにインターフェイスに定義してやればよい。上記のEventDispathcerクラスのメソッドを扱いたい場合は、インターフェイスを以下のように再定義してやる。

package{
    import flash.events.EventDispatcher;
    
    public interface IDoSomething{
        function do():void;
        
        //New!!
        function get eventDispatcher():EventDispatcher;
    }
}

そして、実際のメソッド内容を子クラスに追加、戻り値を子クラス自身とする。子クラスはEventDispatherクラスを継承しているところがポイント。

package{
    import flash.events.EventDispatcher;
    
    public class DoSomething extends EventDispatcher implements IDoSomething{
        
        //New!!
        public function get eventDispatcher():EventDispatcher{
            return this;
        }
        
        public function do():void{
            //Do something
        }
    }
}

mainクラスは以下の通り。IDoSomething.eventDispatcher()メソッド経由でEventDispathcer.dispatchEvent()メソッドにアクセス。無事コンパイルが通る。

package {
    import flash.display.Sprite;
    import flash.events.Event;
    
    //main
    public class main extends Sprite{
        //Defined as Interface
        private var _doSomething:IDoSomething;
        
        public function main(){
            _doSomething = new DoSomething();
            
            //Event successfully dispatched!!
            _doSomething.eventDispacther.dispatchEvent(new Event("do"));
        }
    }
}

応用

同じようにして、例えば子クラスにDisplayObjectContainerクラスを継承させて、DisplayObjectContainerクラスを戻り値とするメソッドを定義してやれば、インターフェイスからDisplayObjectContainer.addChild()などを使うことができる。

package {
    import flash.display.Sprite;
    
    //main
    public class main extends Sprite{
        public function main(){
            var example:IExample = new Example();
            var testSprite:Sprite = new Sprite();
            
            //The interface acts as Sprite!!
            example.displayObjectContainer.addChild(testSprite);
        }
    }
}

package{
    import flash.display.DisplayObjectContainer;
    
    public interface IExample{
        function doExample():void;
        function get displayObjectContainer():DisplayObjectContainer;
    }
}

package{
    import flash.display.Sprite;
    import flash.display.DisplayObjectContainer;
    
    public class Example extends Sprite implements IExample{
        public function doExample():void{
        }
        
        public function get displayObjectContainer():DisplayObjectContainer{
            return this;
        }
    }
}

***********************

追記 2008/12/14

やべえ。インターフェイスにはIEventDispatcherインターフェイスを継承させて、子クラスにはEventDispatcherクラスを継承させるだけで無茶苦茶スマートな構造になった。mutaさん thanks!!

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

    public class main extends Sprite{  
        public function main(){         
            var doSomething:IDoSomething = new DoSomething();
            doSomething.addEventListener(Event.COMPLETE,trace);
            doSomething.dispatchEvent(new Event(Event.COMPLETE));
        }
    }
}

package{
    import flash.events.IEventDispatcher;

    public interface IDoSomething extends IEventDispatcher{
    }
}

package{
    import flash.events.EventDispatcher;

    public class DoSomething extends EventDispatcher implements IDoSomething{
    }
}

Viewing latest article 5
Browse Latest Browse All 10