Прокси всемогущий

Есть в недрах 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

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
question for bots )
Image CAPTCHA
Enter the characters shown in the image.