こんにちは。きんくまです。
AS3を勉強しはじめたころ、interfaceの役割が全然わかりませんでした。
それで、最近わかってきたと思うので、書いてみようと思います。
interfaceって何?
簡単にいうと、「合い言葉(interfaceの関数名)を決めておくから、合い言葉を言われたら、自分で決めておいたこと(各自の実装)をそれぞれしてね。」
って言う感じでしょうか。
それで「これらのバラバラに実装されたオブジェクト同士をまとめて動作させたい時」に非常に有効だということです。Arrayとかに全部ごっちゃまぜにつっこんで、interfaceで決めた関数を呼び出すときに非常に役に立ちます。
あと、「interfaceに書いてあること以外に何ができるか知らないけど、interfaceに書いてあることは絶対にできるはずだから、やらせちゃえ!」みたいなことも可能です。これはあとで説明します。
例えば、IMovableっていうinterfaceを作るとします。
これは動くことができるのを定義します。今回はmoveという1つの関数だけを定義しましたが、いくつ入れてもOKです。
package { public interface IMovable { function move():void; } }
車、飛行機、船の3つのクラスがこのinterfaceを実装するとします。
package { import flash.display.Sprite; public class Car extends Sprite implements IMovable { public function Car() { super(); } public function move():void { trace("地上を走っちゃう実装だよ!"); } } } package { import flash.display.Sprite; public class Airplane extends Sprite implements IMovable { public function Airplane() { super(); } public function move():void { trace("空を飛んじゃう実装なんだ!"); } } } package { import flash.display.Sprite; public class Ship extends Sprite implements IMovable { public function Ship() { super(); } public function move():void { trace("海の上をすーいすーい!!"); } } }
moveの中身はtraceの文字列を書き換えただけですが、実際には画面を書き換えたり、プロパティをいじったりする実装が入ると思ってください。
大事なのはこれらをまとめる段階です。
package { import flash.display.Sprite; public class InterfaceExtends extends Sprite { private var _movables:Array; public function InterfaceExtends() { _movables = []; var car:Car = new Car(); var plane:Airplane = new Airplane(); var ship:Ship = new Ship(); _movables.push(car, plane, ship); //ここが大事なところ var movable:IMovable; for(var i:int = 0; i < _movables.length; i++){ movable = _movables[i]; movable.move(); //これがやりたいだけ } } } }
最後のところで、IMovable型のmovableという変数を宣言して、movable.move()を呼んでいます。
これがキモなのかなと。
Car,Airplane,Shipの3つのクラスはinterfaceが共通という他に、共通点はありません。
(Spriteだっていうのはおいておくとして)
こういう一見関係性がないようなクラスをまとめて動作させたいときがあります。
例えば、ツールアイコンがいくつかならんでいて、そのアイコンを押すと、そのツールのパネルが開くとします。
パネルの操作中の動作の実装はそれぞれに違うんですが、パネルを「開く」のと「閉じる」という動作は共通です。
それで、「開く」と「閉じる」をinterfaceに定義して(IToolPanel)、それらのツールパネルはそれらを実装する(implement)とします。
それらをArrayにキープしておけば、そのArrayの中に入っているものは、確実に「開いて」「閉じる」ことができるわけです。
また、Arrayにキープしないで、ある変数をどこかに持つとして、その変数の型をIToolPanel型にすれば、IToolPanel型を実装しているクラスであれば、どんなクラスでも代入ができるんです。
これは便利なことです。その代入されてるオブジェクト自身は、IToolPanelを実装しているものが代入されているっていうこと以外、実は何が代入されているかわからないのです。
さっきの例に戻ると
package { import flash.display.Sprite; public class InterfaceExtends extends Sprite { private var _movables:Array; public function InterfaceExtends() { _movables = []; var car:Car = new Car(); var plane:Airplane = new Airplane(); var ship:Ship = new Ship(); _movables.push(car, plane, ship); var movable:IMovable; movable = car; //これでも movable.move(); movable = plane; //これでも movable.move(); movable = ship; //これでもOK movable.move(); } } }
それで、何が良いかというと「知らなくていい情報は知らない方がいい。その方がお互いにいい距離を保てる」ということです。なんだか男女の仲のようですが、個人的にはオブジェクト指向の目的の1つはこれなんじゃないかと思っています。
個別の情報を知っているよりも、抽象化された情報だけを知っている方がオブジェクト同士の関係は疎結合になります。
疎結合になればなるほど、他のオブジェクトに適用しやすくなり、汎用性が高まります。
逆に細かい情報を載せれば載せるほど、そのオブジェクトの動きは固くなっていき、汎用性は下がります。
だから、平たく言うと汎用性の高いものは、他のときにも使えていいよね!ということとも言えます。
ベースとなる汎用性の高いものをあらかじめ作っておけば、あとは個別の条件とその間を埋めるものに手をかければよいと。
なんだか話がそれましたけど、最近「なるべく疎結合になるように」という方針をどこかで考えながらプログラムしています。
さて、interfaceについていいよ!と書いてきましたけど、使い過ぎもダメかと。無理矢理適用させるとかダメかと。
「実装の中身がバラバラだけど、こいつらまとめてなんとかしないと!」という時に、「そういえば、interfaceってあったっけ?」ぐらいかなと思ってます。
あ、そうだ、あとiOSとAndroidのネイティブアプリの開発してて思ったのだけど、これもinterfaceが出てきてて(iOSの場合はprotocol)それの場合は、今回紹介したのとはちょっと違う使い方です。感覚的にはAS3のEventDispatcherみたいなのだけれど、、。ある特別なクラスがあって、そこからイベントを受け取ろうとしたときに、そのクラスで定義されているinterfaceを実装してあげるとそれが呼ばれてヤッホーいみたいな。とにかくそんな感覚です。
こういう使い方は、自分はASでは全然したことないけど、ひょっとしてこっちがinterfaceの機能の本命だったりして。
なんか自信ありげにエントリ書いてみたけど、後から不安になってきた。
とにかく、、それで、もう気がついている方もいらっしゃると思いますが、オブジェクト指向の「継承 extend」を使うことでinterfaceと同じような目的を果たすことができます。これは次回のエントリーで書きます。
ではでは。
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ