По ходу дела возник вопрос, что же такое интерфейсы в AS3, зачем они нужны, и как ими пользоваться?
Прежде всего, интерфейсы — это то, что затрагивает тему объектов, классов и прочего арсенала объектно-ориентированного программирования. Они не имеют непосредственного отношения к пользовательским интерфейсам (UI) и к разным соединениям с физическими устройствами (USB, FireWire и т.п.).
Создавая объект, мы должны позаботиться о том, чтобы этим объектом можно было пользоваться. Пользоваться — значит, прежде всего, как-то с ним общаться, взаимодействовать. Для взаимодействия с объектом требуется знать: 1) какие методы в нем определены для этих целей, 2) что можно задавать таким методам в качестве параметров, и 3) какое значение такие методы возвращают. Вот эту связку 1-2-3 называют сигнатурой метода, а множество всех сигнатур объекта — интерфейсом.
Конечно, можно было бы объявить эти методы, составляющие интерфейс, просто как public, и вызывать их извне. Но случается так, что во взаимодействии участвуют объекты совершенно непохожие друг на друга, даже не имеющие общего предка. Тем не менее, они участвуют в общении на равных, в том смысле, что вызовы происходят на стадии выполнения стандартизированным способом.
Можно привести такую аналогию. Допустим, в поисках работы flash-дизайнер Вася Пупкин рассылает резюме во все известные конторы. Он знает, что резюме можно отсылать по e-mail, по факсу, письмом или, на худой конец, принести его самостоятельно. Он знает, что если обернуть своим резюме кирпич и бросить его в окно потенциальному работодателю, его кандидатуру вряд ли рассмотрят. В то же время, работодатели ожидают видеть резюме, где написаны имя, возраст, профессия, координаты и опыт работы, и не ожидают увидеть там изрезанную маникюрными ножницами передовицу Times. Причем эти работодатели могут быть крупными и мелкими компаниями, местными или забугорными, какими угодно, и могут ничего не знать друг о друге. Процедуры рассмотрения резюме тоже могут быть самыми разными.
Чтобы нормальная коммуникация состоялась, и резюме Васи Пупкина было рассмотрено, все участники данного процесса следуют сложившимся традициям в данном виде общения. В объектно-ориентированном мире роль таких жестких традиций или стандартов общения играют интерфейсы. Причем интерфейсы не определяют конкретные способы обработки информации, они лишь позволяют коммуникации состояться. То есть, сигнатур без реализации вполне достаточно.
Во Flash Player API определен интерфейс IEventDispatcher, который любой класс может использовать для обработки объектов-сообщений. Его объявление выглядит он так:
public interface IEventDispatcher { function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean = false):void; function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void; function dispatchEvent(event:Event):Boolean; function hasEventListener(type:String):Boolean; function willTrigger(type:String):Boolean; }
Кодирование методов лежит уже на классах, которые реализуют данный интерфейс. Но для начала нужно как-то «подцепить» интерфейс к классу. Делается это с помощью слова implements:
public class EventDispatcher implements IEventDispatcher { function dispatchEvent(event:Event):Boolean { /* реализация метода */ } ... }
Здесь EventDispatcher должен реализовать, т.е. заполнить программным кодом каждый объявленный в интерфейсе IEventDispatcher метод. Соответственно, нельзя получить экземпляр интерфейса IEventDispatcher, но можно получить экземпляр класса EventDispatcher.
Интерфейсы не могут содержать свойств и констант, но могут определять геттеры и сеттеры.
Чтобы объявить интерфейс, потребуется ключевое слово interface. Рассмотрим интерфейс IExternalizable из пакета flash.utils, который определяет протокол для сериализации объекта, т.е. перевода его в формат, пригодный для транспортировки по сети и хранения на дисках.
public interface IExternalizable { function writeExternal(output:IDataOutput):void; function readExternal(input:IDataInput):void; }
Обратите внимание, что интерфейс IExternalizable объявлен со спецификатором доступа public. Интерфейсы могут объявляться только со спецификаторами public and internal, а методы внутри них не снабжаются вообще никакими спецификаторами.
Принято называть интерфейсы начиная с заглавной I, но это не жесткое требование. Часто объявления интерфейсов помещают на верхний уровень пакета. Их нельзя совмещать с объявлениями классов или объявлениями других интерфейсов.
Интерфейсы могут наследовать от одного или нескольких других интерфейсов. Например, в следующем примере IExample расширяет интерфейс IExternalizable:
public interface IExample extends IExternalizable { function extra():void; }
Любой класс, который подключит интерфейс IExample, должен реализовать не только метод extra(), но и методы writeExternal() и readExternal(), унаследованные от IExternalizable.
В ActionScript 3.0 класс может подключать более одного интерфейса. В следующем примере определяются два интерфейса, IAlpha and IBeta, и класс Alpha, который реализует оба интерфейса:
interface IAlpha { function foo(str:String):String; }
interface IBeta { function bar():void; }
class Alpha implements IAlpha, IBeta { public function foo(param:String):String {} public function bar():void {} }
Методы класса, реализующего интерфейс, должны делать следующее:
- использовать спецификатор public
- использовать те же имена, что определены в интерфейсе
- иметь то же число параметров, и параметры того же типа, что определены в интерфейсе
- возвращать значение заданного в интерфейсе типа
Однако, допускается некоторая гибкость. Хотя количество параметров в методах и их тип должны совпадать с объявленными в интерфейсе, названия параметров могут быть иными. Например, в предыдущем примере параметр метода Alpha.foo() называется param:
public function foo(param:String):String {}
Хотя в интерфейсе этот параметр называется str:
function foo(str:String):String;
Также есть некоторая гибкость со значением параметров по умолчанию. Объявление интерфейса может включать определения методов со значением параметров по умолчанию. В таком случае метод, реализующий такое объявление в интерфейсе, также должен иметь значение по умолчанию, того же типа, но само значение может быть иным. Например, в следующем примере в интерфейсе объявляется метод с параметром, значение которого по умолчанию равно 3:
interface IGamma { function doSomething(param:int = 3):void; }
А реализация этого интефейса задает другое значение параметра по умолчанию:
class Gamma implements IGamma { public function doSomething(param:int = 4):void {} }
Причина таких допущений в том, что правила реализации интерфейса разработаны с особым вниманием к совместимости типов данных, а требование соблюдать совпадение имен параметров не является обязательным для соблюдения такой совместимости.
