Есть в недрах API флэш плеера такой хитрый класс, как flash.utils.Proxy. И сча мы с его помощью сотворим немного программистской магии.
Суть его в том, что он позволяет обрабатывать обращения к несуществующим свойствам и методам объекта и делать что-нибудь интересное с такими обращениями. Фича известная в мире динамической типизации, реализована во многих языках такого типа, начиная еще с дедушки ООП -- SmallTalk. Ну и в AS1-2 это было. Как оказалось, и в AS3 сохранилось :)
Вы, конечно, скажете, что такие обращения не пропустит компилятор. И это правда. Если, конечно, ваш класс не объявлен как dynamic :)
Ну к делу. Итак, у нас есть класс FmsService, который посылает запросы на FMS сервер. Часть этих методов чем-то уникальны, но их не много. Большинство же методов выглядит как-то так:
public function SendMessage(roomID : String, message : MessageVO) : void
{ nc.call('SendMessage', null, roomID, message); }
public function ChangeColor(color : int) : void
{ nc.call('ChangeColor', null, color); }
public function ChangeStatus(status : int) : void
{ nc.call('ChangeStatus', null, status); }
Налицо boilerplate код, от которого сразу же хочется избавиться. Например так:
public function Call(methodName : String, params : *) : void
{
nc.call(methodName, null, params);
}
Тут возникают некоторые неудобства с переменным числом параметров, но они вполне разрешимы:
public function Call(methodName : String, ... args) : void
{
args.unshift(null);
args.unshift(methodName);
nc.call.apply(nc, args);
}
Все бы ничего, можно пользоваться. Разве что использование fmsService стало чуть менее красивым. Вместо привычного
fmsService.SendMessage(roomID, message);
приходится использовать более громоздкое
fmsService.Call('SendMessage', roomID, message);
Но Proxy избавляет нас и от этой проблемы. Если мы унаследуем наш FmsService от Proxy и объявим его как dynamic, то достаточно добавить такой метод:
flash_proxy override function callProperty(methodName : *, ... args) : *
{
args.unshift(null);
args.unshift(name);
nc.call.apply(nc, args);
return null;
}
И будет нам счастье. Волшебным образом начинает работать привычный
fmsService.SendMessage(roomID, message);
Но это еще не все. У нас же проект на фреймворке Mate, а fmsService мы обычно вызываем из EventMap. А EventMap, как оказалось, дергает не callProperty, а hasProperty и getProperty. Так что код придется немного усложнить:
import flash.utils.flash_proxy;
flash_proxy override function callProperty(methodName : *, ... args) : *
{
this[methodName].apply(this, args);
return null;
}
flash_proxy override function hasProperty(name : *) : Boolean
{ return true; }
flash_proxy override function getProperty(name : *) : *
{
return function(... args) : *
{
args.unshift(null);
args.unshift(name);
nc.call.apply(nc, args);
return null;
}
}
И теперь все работает и с Mate. PROFIT.
Но и на этом можно не останавливаться (хотя я пока остановился), а поглядеть на другой класс -- FmsClient, который принимает вызовы с FMS сервера. В основном его методы выглядят как-то так:
public function OnSomethingHappened(param1 : *, param2 : *) : void
{
var event : SomeCustomEvent = new SomeCustomEvent();
event.param1 = param1;
event.param2 = param2;
dispatcher.dispatchEvent(event);
}
Очевидно это тоже шаблонный код, где вызов метода со стороны FMS сервера преобразуется в какое-то кастомное событие, которое потом будет обрабатываться в EventMap.
Тут тоже можно применить прокси как-нибудь так:
flash_proxy override function callProperty(methodName : *, ... args) : *
{
var event : FmsEvent = new FmsEvent(methodName);
event.data = args;
dispatcher.dispatchEvent(event);
}
Ну и разгребать это в EventMap как-нибудь так:
<mate:EventHandlers type="{FmsEvent.SomethingHappened}">
<!-- do something -->
</mate:EventHandlers>
<mate:EventHandlers type="{FmsEvent.SomethingOtherHappened}">
<!-- do something other -->
</mate:EventHandlers>
PROFIT очевиден -- пропадает шаблонный код в классах FmsService и FmsClient, а код в EventMap почти не меняется.
За передовую программистскую магию!
UPDATE: Все эти замыслы реализованы тут https://github.com/yzh44yzh/rtmp-service-mate-ext. И там все сделано даже лучше, чем тут описано :)
Add new comment