Просмотр полной версии : Неудобная организация сервера. Коллбеки
Коллеги, я уж зажрался наверное, но привык работать на приятном исполнении сервера. А тут мне вот что дают :
Вызываю гетом/постом - некий скрипт сервера, получаю ответ. Это все хорошо, все понятно.
В ответ только приходит JSON - в котором не указана команда которую вызвали.
То есть какой бы я запрос не делал на данный сервер - я не могу знать в итоге, какой именно я запрос делал.
Сейчас это общий случай
private function onRequestComplete(e:Event):void
{
trace("Data recieved : " + this._urlLoader.data);
this.dispatchEvent(new ServerEvent(ServerEvent.DATA_RECIEVED , this._urlLoader.data));
}
Но мне соотвественно оно не пойдет, ибо я должен знать какой был запрос, чтобы подписаться там где нужно. Как здесь использовать коллбеки я тоже не представляю. Допустим 1 запрос идет 10 секунд, а другой 2 секунды, какой придет быстрее неизвестно и push(callBack) - нам не поможет, ибо не будем знать какой из коллбеков брать с массива, так как не знаем и какой запрос выполнился)))
Кто нить работал с такми ПРОГРЕССИВНЫМ!!!! апи... и как выходили из ситуации в рамках ООП, а не костылях
Класс - операция
Создаете объект этого класса, вызываете метод с параметрами. Он внутри нужно запоминает, сам делает запрос, и сам обрабатывает ответ и выдает вам, в каком угодно виде.
Типа
(new Oper(url, params, 'user_info', callback)).run();
(new Oper(url2, params2, 'user_friends', callback)).run();
Также можно вообще урл зашить в операцию и делать предобработку ответа в классе, приводить джсон к списку друзей или юзеринфе и в коллбек выкидывать готовый для работы объект(ы).
GBee - вот такой вот подход обычно и является другом девочки *утечки памяти*. Я естественно понимаю, как это сделать. Тут именно вопрос как сделать красиво, не пересоздавая каждый раз классы
Вам шашечки или ехать? Это просто обертка над лоадером, вы же все равно новый лоадер создаете.
Можно забацать маппинг тип=запрос+параметры (в хмл или хардкодом или как угодно) и автоматизируете все это, чтобы само все там что-то делало. У вас же все равно где-то есть вызов. Непонятно, у вас урлы что ли не отличаются никак? Тыкаетесь в один, а он разные ответы шлет?
Т.е вам не пришел комплит от старого запроса, а вы уже новый реквестите. Прикольно.
Т.е вам не пришел комплит от старого запроса, а вы уже новый реквестите. Прикольно.
Эмн, а в чем сарказм?
Только в том если последовательность ответов имеет значение.
Dukobpa3
10.01.2014, 04:51
Я с Пчолом согласен.
Тут "команда" спасет. Один запрос - одна команда. (Либо нечто похожее на RPC.)
Ты ее вызвал - она сама отработала, сама результат получила, сама разобралась что с ответом делать.
Чтобы не было утечек нужно предусмотреть какой-то CommandController. В котором будет храниться список текущих активных команд. Плюс например сама команда будет в это контроллер диспатчить "комплит" по завершению обработки ответа, а контроллер ее грамотно дестроить.
Хотя я бы по голове серверщикам настучал лучше, ибо все мои привычные подходы подразумевают асинхронный подход к общению с сервером. Но если бы "був час та натхнення" - то заморочился бы так как описал.
я просто делаю массив (колбэков или типов событий для диспатча) и массив реквестов, пушу все в массив , и начинаю загрузку, с нулевого элемента, когда приходит ответ, делаю то что надо с ответом и колбек функцией, и делаю шифт массивов, и перевызываю функцию загрузки.
Другими словами, ты создаешь очередь и сдвигаешь ее по мере прихода комплита, посылая очередной реквест от следующего элемента в очереди. Если, так, то правильно. Только непонятно, что такое "пушить асинхронно".
имелось ввиду, что можно сразу много запросов добавить из любой части программы, а не ждать выполнения каждого, чтобы добавить следующий
Слово "асинхронно" не несет никакой смысловой нагрузки, потому как непонятно как еще можно формировать очередь на одном клиенте. Кстати, в соседней ветке правильный подход показан с флоксом.
удалю его раз оно вас так напрягает)
Спасибо парни. Значит будем работать по методу пчела и дикобраза вместе.
Насутчать им по пальцам надо конечно, но что поделать - говорят у нас уже как сто лет двиг написан и мы, такие вот умные - с нашим АПИ уже все работабют и никто не жалуется ( типа на разных языках ) .
Добавлено через 38 минут
Если кому интересно, как я сделал
package com.combet.controllers
{
import com.combet.events.ServerEvent;
/**
* ...
* @author in4core progression lab
*/
public final class ServerProcessor extends Object
{
public static const SERVER:String = "";
public static const AUTHORIZE:String = "";
public static const GET_GAMES_FIELDS:String = "";
public static const GET_MENU_HEADERS:String = "";
public static const GET:String = "get";
public static const POST:String = "post";
public function ServerProcessor()
{
}
public static function runProcess(name:String, callBack:Function , args:Object = null, reqMethod:String = GET):void
{
var sc:ServerConnection = new ServerConnection();
sc.callBack = callBack;
sc.execute(SERVER + name, args, reqMethod);
}
}
}
package com.combet.controllers
{
import com.combet.events.ServerEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
/**
* ...
* @author in4core progression lab
*/
public class ServerConnection extends EventDispatcher
{
private var _urlLoader:URLLoader = new URLLoader();
private var _callBack:Function = null;
public function ServerConnection()
{
this._urlLoader.addEventListener(Event.COMPLETE, onRequestComplete);
}
private function onRequestComplete(e:Event):void
{
trace("Data recieved : " + this._urlLoader.data);
this._urlLoader.removeEventListener(Event.COMPLETE, onRequestComplete);
this._callBack(this._urlLoader.data);
}
public function execute(command:String , args:Object = null , method:String = "get"):void
{
var req:URLRequest = new URLRequest(command);
if (method === "get") req.method = URLRequestMethod.GET;
else if(method === "post") req.method = URLRequestMethod.POST;
if(args) req.data = args;
this._urlLoader.load(req);
}
public function set callBack(value:Function):void
{
this._callBack = value;
}
}
}
Единственно, что я бы хотел занулять после всего этого весь использованный ServerConnection , но думаю GC и сам соберет его, раз ссылок не остается так и так.
Dukobpa3
10.01.2014, 13:53
Лучше бы не показывал.
С этого момента уже весело стало.
extends Object
По пальцам стучать он собрался.
this._param
Примерно переводится как "Мой мой только мой приватный параметр"
Dukobpa3
10.01.2014, 14:38
Короче конструктивнее отвечу, а то совесть мучает.
0. То что ты показал - дефолтное решение, с которого ты и начал, тупо хрень которая вместо подписки на комплит дает регистрацию коллбеком. Хотя суть реально та же. Твоя обертка ничем не помогает решить вопрос.
1. Кто угодно, откуда угодно может заспамить месадж с какой угодно обработкой(коллбеком).
2. ServerConnection - раз уж на то пошло - лучше сделать интернал классом, чтоб напрямую к нему доступа не было. Только через твою фабрику-манагер.
3. Никаких подписок на ерроры и таймауты.
4. Никакого кеша/пула/очереди. Тупо создал лоадер в небытие и забыл про него.
5. Удаляться мусорщиком твои лоадеры не будут из-за колбека. Возможность "отписки" не предусмотрена.
6. Создание лоадера происходит идентично дефолтному - тупо урл гет-пост и параметр. Человеческий фактор не исключен ни на грамм.
7. Коллбеки нетипизированные (еще один пункт к человечкому фактору). Типизация коллбека во флеше проблематична. Но никто не отменял стратегию или как минимум интерфейс, ограничивающий г-нокод в этих местах.
Итого вопрос: А что ты сделал то? И зачем? С таким подходом - чем урллоадер напрямую хуже, кроме экономии копипасты пяти строк кода подписки на комплит?
Добавлено через 6 минут
А столько текста и негодования я вылил по этому вопросу потому что в нашем проекте умельцы запилили такую же систему.
И уж поверь мне. Она не рабочая. Или делай нормально или лучше юзай лоадер напрямую.
Akopalipsis
10.01.2014, 14:58
5. Удаляться мусорщиком твои лоадеры не будут из-за колбека. Возможность "отписки" не предусмотрена.
А разве в таком случаи, калбек помешает удалению? Мне почему-то всегда казалось, что вот если бы на сам класс у кого-то ссылка была, то это помешало удалению.
Dukobpa3
10.01.2014, 15:02
Ну с этим я может и загнался.
Остальное в силе.
1. Кто угодно, откуда угодно может заспамить месадж с какой угодно обработкой(коллбеком).
Что значит кто угодно? Кто ? о чем вообще речь? Никто никуда заспамить ничего не может, к серверным скриптам обратиться нельзя, пока не пройдена авторизация логина-пароля. Или ты про само приложение и работы над ним? Нет нет, тут речь об этом не идет, все предельно просто, никаких сложных структур, чтобы можно было нарваться на ошибку.
Тут о безопасности речь не идет, она нас не беспокоит. Тут именно стоял вопрос, чтобы не было проблем, что какое то соыбтие придет раньше и мы не знаем какое. А тут все собственно понятно. Пускай коллбеки, ну куда деваться.
А чего у вас там в проекте столо не рабочим то? Вполне себе быдло-подход на быдло-сервер, все логично.
extends Object Всегда пишу классам не имеющим эестенд на что то другое, в любом случае он наследник Object - как ни крути, но выглядит приятнее ПОТОМУ, что у нас все таки типизированный язык, позволяющий указать типизацию даже в пустышках.
На остальные 7 пунктов отвечать не буду, ты прав - но ты прав в контесте своего проекта, тут так скажем совсем мелкая поделка в 100 строчек, а не огромная игра, чтобы парится.
А вопрос я задал, ибо мне было интересно , как подругому оно могло решаться.
Dukobpa3
10.01.2014, 15:43
Я ничего не имею против этого куска кода в контексте мелкой поделки. Вполне рабочий кусок.
Но зачем тогда выкладывать?
Чтоб тот же апокалипсис подумал что это норм подход и так и дальше надо?
Akopalipsis
10.01.2014, 16:01
Чтоб тот же апокалипсис подумал что это норм подход и так и дальше надо?
Akopalipsis'a таким куском не вдохновить, он слишком для меня простой :)
я когда думаю о связи с сервером, то почему-то в сторону команд смотрю, не знаю на сколько это правильно. Но был бы рад, если бы мне сказали названия готовых решений, которые можно взять для изучения за идеал. Flox-клиент, это то, что нужно для связи приложения с сервером?
Dukobpa3
10.01.2014, 16:04
Я думаю это отдельная тема. Хочешь - начни. А тут оффтопить нефиг.
Dukobpa3 - так покажи как надо! Мне вот лично в голову не пришло как здесь сделать событийную модель, может быть не дорос еще, кроме как колбечный порт я ничего не вижу лучше в данной ситуации, а безусловно хочется
if(command=="AUTH") dispatchEvennt(...)
else if(command == "GET_FIELDS") dispatchEvent(...)
Чтобы на каждую команду была подписка, и нужный нам контроллер подписывался на событие и ждал его. Так вот хренушки тут так сделать, лично я не смог найти такого решения. Если ты более компитентен в этом вопросе - покажи поделку
Чтоб тот же апокалипсис подумал что это норм подход и так и дальше надо?
выложите свой кусок , чтобы было понятно как надо..,
если честно я делаю не колбек функциями, а диспатчу событие с пришедшим объектом, а главный контроллер подписан на разные события и делает то что надо, не знаю какой подход правильней , колбэчный или с диспатчем, а если и тот и тот туфта, какой тогда правильный? Желательно с МВЦ-шной стороны
Чтобы на каждую команду была подписка, и нужный нам контроллер подписывался на событие и ждал его.
видел много вариаций на эту тему, видел много раз что вообще модель должна с сервером общаться (а не контроллер), но так и не смог докопаться до истины, я тоже контроллером ловлю.
но сюда вот из этой схемы http://www.flasher.ru/forum/attachment.php?attachmentid=27940&d=1336316055 у контроллера должна быть прямая ссылка на загрузчик, и они должны обмениваться коммандами, а не контроллер должен слушать загрузчик...
Akopalipsis
10.01.2014, 16:40
А если нужна очередь, то может id сделать и проверять на currentComplete.
Да и подписку можно по id сделать. Но так как я ещё такого не делал, то согласен, что весу в моих словах - не о чём :)
А если нужна очередь, то может id сделать и проверять на currentComplete.
Да и подписку можно по id сделать. Но так как я ещё такого не делал, то согласен, что весу в моих словах - не о чём Ага, очередной костылидзе, тогда и коллбеками обойдемся
Isfet, я бы новый реквест посылал в хэндлере сервисного контроллера где бы и ловил расширенный эвент с urlLoader.data...
Добавлено через 7 минут
Akopalipsis, Вы совешенно правильно рассуждаете. И тут возможно даже не только id, а целый путь к id нужного вида. Все зависит от того уникальный у вас id для вида или вы его индексируете. Допустим, некоторые юниты у Вас повторяются в разных локациях.
я бы новый реквест посылал в хэндлере сервисного контроллера где бы и ловил расширенный эвент с urlLoader.data...
вы всегда так сжато и ёмко даете ответы, что я порой не могу ничего понять.
как я уже понял, из ранней с вами беседе, сервисный контроллер - это контроллер который общается с сервером, но у нас также есть главный контроллер. Главный контроллер слушает сервисный контроллер , и когда в сервисный контроллер приходят данные с сервака, он в хендлере onComplete проверят данные на ошибки и диспатчит событие с пришедшими данными - > главному контроллеру и делает следующий реквест. Главный контроллер подписан на все возможные события и в хендлерах делает с данными то что ему нужно, поправьте меня если вы имели ввиду что-то другое.
Akopalipsis
10.01.2014, 18:23
Ага, очередной костылидзе, тогда и коллбеками обойдемся
Возможно, я же не делал. Но вот в этой теме, мне немного понравился подход с созданием класса.
Создал класс, зарядил его ссылкой и прочим и в словарь с id. И допустим он был по очереди id=2, а с сервера пришло для id=4? но нужно третий. По этому проверяешь currentID и если он равен 2, то мы просто ждем третий, а когда третий придёт и мы его используем, то проверим, есть ли там следующий.
И мне кажется что это единственный-оптимальный способ соблюдения очереди. Но вот только если на деле запросы частые, и на деле разница доли секунд, то словари, id и прочее только замедлят работу.
Добавлено через 5 минут
вы всегда так сжато и ёмко даете ответы, что я порой не могу ничего понять.
По началу я тоже считал Babylon странным и читая посты, где над ним немного подсмеивались из-за "ядра", тоже улыбался. Но как только я шагнул в сторону фраймворков, то сразу мне стали не понятны слова непонимания, так как там как раз везде и есть это ядро.
Правильно. Контроллеры общаются друг с другом посредством диспатчей, а обрабатывают события в хэндлерах, где и передают данные сервис<-> вид, либо сервис<->логика(модель).
Dukobpa3
10.01.2014, 18:53
подсмеивались из-за "ядра"
Смеялись не из-за ядра, а из-за хмл-мвц:)
Да и мне до сих пор система не понятна, а пример бы посмотрел. Теоретически должно адекватно смотреться в связке с флексом.
Я учёл пожелания. Теперь у меня по другому сделано. Есть дикт из коллекций MVC которые имеют свойства INodeIn и INodeOut c интерфейсом INode, где INode может быть либо XML, либо JSON. Пока еще не допилено...
Dukobpa3
10.01.2014, 19:52
Dukobpa3 - так покажи как надо!
выложите свой кусок , чтобы было понятно как надо..,
У меня есть вот такой вот пакетик (https://github.com/eggs-gd/eggs-net-as3). Пользуюсь уже несколько лет.
Недавно наводил порядки там и выпилил немного костыльного функционала, в том числе и криво написанный хттп-коннектор, в итоге там остался реализован только сокет(хттп уже давно не использовал, так я и не переживал).
Но я планировал добавить туда и хттп-коннектор. Возможно как раз будет повод.
Я честно вот сегодня пытался на костылях нечто собрать чтоб сюда скинуть, но костыли мне не понравились. Сделаю адекватно, расширю функционал своего пакета. Можно за репозиторием следить.
Добавлено через 4 минуты
Костыли частично показать могу:
Абстрактная команда. Ее расширяют наши остальные конкретные команды.
package connector.commands
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import project.model.SomeModel;
internal class Command extends EventDispatcher
{
protected static const SERVER_URL:String = "http://your.server.url/";
protected var _urlLoader:URLLoader = new URLLoader();
protected var _urlRequest:URLRequest = new URLRequest();
// Каким-то образом ссылка на модель должна попасть в команду.
// не хочу об этом думать. new SomeModel точно не конает, потмоу что она должна быть одна у всех.
// Но это как вариант, и абсолютно не факт что ссылка на модель будет в базовой команде.
// так как каждая команда скорее всего будет работать с другой моделью.
protected var _model:SomeModel = new SomeModel();
final public function execute(data:Object):void
{
prepareLoader(data);
_urlLoader.addEventListener(Event.COMPLETE, onRequestComplete);
_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
_urlLoader.load(_urlRequest);
}
protected function prepareLoader(data:Object):void
{
throw new Error("Should be overriden by sub classes");
}
protected function processData(data:Object):void
{
throw new Error("Should be overriden by sub classes");
}
private function onRequestComplete(e:Event):void
{
_urlLoader.removeEventListener(Event.COMPLETE, onRequestComplete);
_urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
processData(_urlLoader.data);
}
private function onError(event:IOErrorEvent):void
{
trace("error");
}
}
}
Команда авторизации:
package connector.commands
{
import project.model.data.AuthResponceDo;
internal class AuthCommand extends Command
{
private static const SERVICE_URL:String = "auth.php";
override protected function prepareLoader(data:Object):void
{
// Эта команда простенькая, ниче настраивать не надо кроме урла.
_urlRequest.url = Command.SERVER_URL + SERVICE_URL;
}
override protected function processData(data:Object):void
{
// парсинг будет совсем не такой, так как понятно что с сервера прилетит не этот тип Если у нас не амф.
// для примера покатит.
var parcedData:AuthResponceDo = data as AuthResponceDo;
// Здесь просто вызывается метод модели. Но может быть любой другой набор действий.
_model.auth(parcedData);
}
}
}
Команда хода в бою:
package connector.commands
{
import connector.commands.data.BattleTurnDto;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import project.model.data.TurnResponceDo;
internal class BattleTurnCommand extends Command
{
private static const SERVICE_URL:String = "battle_turn.php";
override protected function prepareLoader(data:Object):void
{
// тут команда посложнее, и она шлет во флешварах данные.
var dto:BattleTurnDto = data as BattleTurnDto;
if (!dto) throw new Error("а нифига, надо передать BattleTurnDto");
_urlRequest.url = Command.SERVER_URL + SERVICE_URL;
var variables:URLVariables = new URLVariables();
variables.battle_id = dto.battle_id;
variables.unit_id = dto.unit_id;
_urlRequest.data = variables;
_urlRequest.method = URLRequestMethod.GET;
}
override protected function processData(data:Object):void
{
// см. авторизацию. Суть та же. Это фейк.
var parcedData:TurnResponceDo = data as TurnResponceDo;
_model.turn(parcedData);
}
}
}
Вспомогательный класс структурка для команды хода:
package connector.commands.data
{
public class BattleTurnDto
{
public var battle_id:String;
public var unit_id:String;
}
}
Суть в том что на каждый месадж создаем отдельный класс который умеет слать запрос на сервер и обрабатывать ответ на него.
Эта часть сравнительно не костыльная получилась и надеюсь идею передал.
А вот по части менеджмента этих команд уже есть варианты. И с этими вариантами начались костыли на решение которых я уже не захотел тратить больше времени чем планировал.
Добавлено через 11 минут
Вот пример использования костыльного манагера, который я показывать не хочу:)
CommandManager.init();
CommandManager.executeCommand(CommandList.COMMAND_AUTH);
var turnDto:BattleTurnDto = new BattleTurnDto();
turnDto.battle_id = "1";
turnDto.unit_id = "10";
CommandManager.executeCommand(CommandList.COMMAND_BATTLE_TURN, turnDto);
Psycho Tiger
16.01.2014, 00:44
Вот пример использования костыльного манагера, который я показывать не хочу
По хорошему, пользовательский код может вполне себе положить на манагер и работать исключительно с командами. То есть, вшить все эти связки с CommandManager в саму команду и сократить код примерно так:
new BattleTurnDto().executeCommand().addCompleteListener(completeHandler);
Такой подход ты считаешь костыльным?
Dukobpa3
16.01.2014, 01:08
I hate JQuery
Добавлено через 46 секунд
Предложенный вариант не избавит от безымянных, непонятно где висящих объектов, а только добавит их.
Добавлено через 3 часа 11 минут
И, кстати, чем вот это:
new BattleTurnDto().executeCommand().addCompleteListener(completeHandler);
Отличается от вот этого (http://www.flasher.ru/forum/showpost.php?p=1156937&postcount=14).
Psycho Tiger
16.01.2014, 11:38
Это не JQuery, а method chaining :)
Можешь набросать примерно, как с точки зрения пользовательского кода выглядит хорошая на твой взгляд система?
Я думаю не сможет.
Тут сам по себе подход сервера убог, а значит сделать под него хорошую систему уже не получится.
Как мне кажется не стоит выдумывать какие то велосипеды на клиенте, когда сервер-сайд хренов. Надо менять сервер.
В момем случае стояла задача сделать БЫСТРО, и чтобы работало. 3-5 дней на реализацию подкладки. Понятное дело, если бы сроки были приемлимые, можно было бы сделать нормальтной эко систему, но тут пришлось безоговорочно работать с тем, что написано уже давно.
Тут сам по себе подход сервера убог, а значит сделать под него хорошую систему уже не получится.
Какой-то вы странный. Вы делаете запрос, вам приходит ответ. Чем подход убог? Не оправдал надежд? Или поломал ваш "универсальный" движок? Велосипеды стоит выдумывать или брать готовые, главное, чтобы задача была выполнена. Перфекционизм хорош в малых дозах, в больших количествах он заставляет писать гневные посты на форумах.
GBee - если ты работаешь с медиа серверами например, у тебя есть серверные методы и сообщения. Там все понятно. Если ты работаешь с серверами типа пхп - то тут сложнее, и требуется удобная организация сервера - когда ты делаешь запрос, в ответе ты должен получать command:What_the_fox_say - чтобы значть на какую конкретно команду пришел ответ. Это не странность - это нормальность. Я же не несу фигни типа - ну а почему на сервере переменную назвали коряво? Это не мое дело. Мое дело получить ответ, который будет понятен и читабелен.
Потрачено.
когда ты делаешь запрос, в ответе ты должен получать command:What_the_fox_say
У вас же есть ссылка на соответствующий стандарт?
Dukobpa3
16.01.2014, 17:11
как с точки зрения пользовательского кода выглядит хорошая на твой взгляд система?
Ну так примерно набросал. Просто внутренности не стал показывать, в виду костыльности.
Ну и после наведения порядка внутри - АПИ тоже может как-то поменяться. (Статик манагер тоже под вопросом, но вот так вот вслепую не могу точно сказать. Надо смотреть на интеграцию в систему. Пальцем в небо сложно.)
CommandManager.executeCommand(CommandList.COMMAND_AUTH);
var turnDto:BattleTurnDto = new BattleTurnDto();
turnDto.battle_id = "1";
turnDto.unit_id = "10";
CommandManager.executeCommand(CommandList.COMMAND_BATTLE_TURN, turnDto);
Добавлено через 1 минуту
Тут сам по себе подход сервера убог
Не убог. Обычная себе синхронная система. Просто я привык к асинхрону и stateless, потому такое не люблю. Но подход нормальный сам по себе.
Psycho Tiger
16.01.2014, 18:53
Ну гм, тогда я тебя не понимаю.
Сожмём твой код:
var turnDto:BattleTurnDto = new BattleTurnDto();
turnDto.battle_id = "1";
turnDto.unit_id = "10";
CommandManager.executeCommand(CommandList.COMMAND_BATTLE_TURN, turnDto);
Внесём в конструктор то, что ты вставляешь сеттерами (филдами?).
var turnDto:BattleTurnDto = new BattleTurnDto(1, 10);
CommandManager.executeCommand(CommandList.COMMAND_BATTLE_TURN, turnDto);
У нас обоих в наших реализациях один запрос – одна команда. Поэтому я вышвыриваю CommandList.COMMAND_BATTLE_TURN из этого кода, внося её в конструктор BattleTurnDto. Зачем? Это DRY. Типа
public function BattleTurnDto(battleID:int, unitID:int){
super();
super.commandName = CommandList.COMMAND_BATTLE_TURN;
}
Поэтому код сжался вот так:
var turnDto:BattleTurnDto = new BattleTurnDto(1, 10);
CommandManager.executeCommand(turnDto);
Теперь я прикидываю, что всегда создавая команду я буду её отправлять. Через CommandManager. Это опять же значит, что я могу создать метод execute() внутри BaseCommand, который будет исполнять вот эту строчку:
CommandManager.executeCommand(turnDto);
(в реальном коде это будет выглядеть как-то так)
public function execute():void{
CommandManager.executeCommand(super.commandName, this);
}
Ну и, конечно, после её исполнения надо где-то обработать ответ. Обычно я делаю это внутри самой команды: она сама находит нужные модели, расфасовывает данные и прочее, но иногда удобно получить коллбек о том, что команда исполнена в пользовательском коде. Добавляется листенер addCompleteHandler.
Вот так рождается строчка:
new BattleTurnDto().executeCommand().addCompleteListener(completeHandler);
На каком шаге это сжатие кода превращает примерно-нормальный в код Александра?
Dukobpa3
17.01.2014, 03:47
Внесём в конструктор то, что ты вставляешь сеттерами (филдами?).
Согласен. Не стал делать в угоду универсальности, а могут быть структурки набором полей поболее двух, тогда конструктор будет унылым.
У нас обоих в наших реализациях один запрос – одна команда. Поэтому я вышвыриваю CommandList.COMMAND_BATTLE_TURN из этого кода, внося её в конструктор BattleTurnDto.
А вот здесь фейл, потому что эту структуру может слать несколько команд.
Или же одна команда может слать разные структуры(Это уже плохой вариант, но мало ли).
Но в любом случае нигде не было оговорено связи структура == команда, ты искусственно эту связь прикрутил.
Поэтому код сжался вот так:
См выше. Если не фейлить - то не сожмется.
Теперь я прикидываю, что всегда создавая команду я буду её отправлять. Через CommandManager. Это опять же значит, что я могу создать метод execute() внутри BaseCommand, который будет исполнять вот эту строчку:
CommandManager.executeCommand(turnDto);
Рассуждения верные, выводы не очень.
Сразу же вопрос - а зачем было добавлять манагер с таким подходом?
Как бы изначально екзекьют и есть в команде. Его наружу проксит манагер, ты берешь обратно заворачиваешь екзекьют через манагер в команде? О_о Логику совсем не уловил. Убери манагер и будет счастье.
Дальше всё строится на этом твоем примере потому чем дальше тем глубже.
Проще сделать таким вот образом, если сильно хочется:
new BattleTurnCommand().execute(new BattleTurnDto(1, 10));
И тогда никакой манагер тебе никуда не упирается (а он в твоей логике таки никуда не упирается, всеми своими доводами ты от него избавиться пытаешься)
//****************************
И я предпочитаю single responsibility.
структурки - тупо типизация чтоб обжекты не гонять.
Команда - команда, ничего более. Разве что благодаря тому что она асинхронная и ждет ответа - в нее инкапсулировали еще и обработку данных.
Менеджер - не знает чем занимаются команды. Просто холдер. В его задачи входит убивать отработавшие команды или наоборот оставлять список созданных команд, чтоб не пересоздавать. Это уже от желаемой логики зависит, как там системе удобнее будет.
Psycho Tiger
17.01.2014, 10:47
И тогда никакой манагер тебе никуда не упирается (а он в твоей логике таки никуда не упирается, всеми своими доводами ты от него избавиться пытаешься)
У меня его и нет. Я его "образно" туда всунул, чтобы поближе к твоему примеру. Мои команды - эдакие микро-контроллеры, сами всё знают и умеют.
Я понял, где у нас расходятся мысли. В моём подходе – одна структура-одна команда в обоих направлениях.
То есть, например есть 3 команды: "Добавить в друзья", "Посмотреть профиль", "Пригласить в мой клан" – и все 3 команды отсылают на сервер одну и ту же структуру – маленькую такую, в одное поле – user_id. В твоём случае – ты хочешь действительно слать одну и ту же структуру (дто), а вот я как раз это и считаю фейлом.
Почему? Потому что это попытка агрегации кода, который агрегирован быть не должен. Если есть константа "4", указывающая на версию протокола и константа "4", показывающая максимальное количество одетых колец – то они как бы равны, но это не значит, что они как-то связаны.
Так и в твоём случае твой подход тебя подведёт, когда при добавлении друга добавиться возможность уведомить его (лишний флаг notice, который не нужен в других двух командах), а при приглашении в клан указывать, какого из типов (клан рабочих/пользовательская гильдия/да всё что угодно). Создание новых Dto, расползание их внутри команд. Это не большая проблема в силу Find Usages в IDE, но я нахожу это неудобным. Поэтому я живу счастливо, интегрируя дто внутрь команды. Такие дела.
И я предпочитаю single responsibility.
По мне, с этим есть проблемы. Вообще, SR говорит что "Один класс – одна обязанность" и в целом, ничего более. Ключевое слово здесь класс: любой класс можно разбить на множество других, каждый из которых будет SR. То есть, если я "вошью" поля из дто прямо в команду – то да, SR может нарушиться. Но если я вошью их в, уже по сути лишнюю сущность дто и уже её вставлю как поле команды – SR будет на месте, но загрустит Оккама.
Dukobpa3
17.01.2014, 15:09
"Добавить в друзья", "Посмотреть профиль", "Пригласить в мой клан" – и все 3 команды отсылают на сервер одну и ту же структуру – маленькую такую, в одное поле – user_id.
Да, но только не структурка "user_id", а что-то типа: user_params.user_id.
И если вдруг появятся user_params_for_invite отдельно от других - появится еще одна структурка. Но не раньше. Потому что велика вероятность что она и не появится. И вот тут вот имеет смысл включать твоего оккама:)
Возьми вот это:
new BattleTurnDto().executeCommand().addCompleteListener(completeHandler);
- Убери из него new, так как каждый раз создавать новую структуру и не следить за ней не ок. Я предполагаю что она в каком-то буфере/холдере/манагере, который либо удалит, либо очистит, либо закеширует после выполнения.
- Сделай так чтоб стартовало не со структуры, а с команды. Потому что по логике ты вызываешь якобы команду, а не данные. И не команду для данных. А В КОМАНДЕ ШЛЕШЬ ДАННЫЕ. Незачем добавлять непонятностей и неочевидностей в код.
- комплит листенер - то с чего начинал ТС. То от чего я пытался уйти. Хочешь вернуть обратно - верни.
получится:
_battleTurnCommand.execute(1, 10).addCompleteListener(completeHandler);
И в этом месте наши мнения сойдутся.
//**********
И я за то что если уж ООП, то лишней ненужной абстрактности добавлять незачем если для этого нет доп-мотивации. Стараюсь объекты соотносить с реальными объектами "из жизни". С логическими сущностями.
Psycho Tiger
18.01.2014, 10:41
- Убери из него new, так как каждый раз создавать новую структуру и не следить за ней не ок. Я предполагаю что она в каком-то буфере/холдере/манагере, который либо удалит, либо очистит, либо закеширует после выполнения.
Ну, конечно. BattleTurn.getInstance(), Pool.getInstance(BattleTurn), new BattleTurn – это всё не так важно в этом конкретном примере. Я стараюсь визуально облегчить примеры для форума.
- Сделай так чтоб стартовало не со структуры, а с команды. Потому что по логике ты вызываешь якобы команду, а не данные. И не команду для данных. А В КОМАНДЕ ШЛЕШЬ ДАННЫЕ. Незачем добавлять непонятностей и неочевидностей в код.
Ага, я упоминал в чате, что я Dto сначала не так понял – расценил как часть имени команды. Конечно, создается команда.
- комплит листенер - то с чего начинал ТС. То от чего я пытался уйти. Хочешь вернуть обратно - верни.
Тебя в них смущает, я так понимаю, исключительно вопрос GC / конвенции.
С коллбеками – если положить их в Dictionary с weakKeys, то даже с повисшим коллбеком не будет проблем. Но откуда он может повиснуть, если при всех ошибках этот коллбек тщательно чиститься ?
Что касается конвенции – можно подписываться Event'ом, опять же с weakKey. Опять же, не смотри на addCompleteListener как какую-то законченную идею: это может быть и сигнал, и эвент. Конкретней рассматривается уже в контексте разрабатываемого приложения.
Zebestov
18.01.2014, 12:04
Может я сейчас что-то не то скажу, но кто мешает пользоваться обычным URLLoader и словарем, где ключ — наш лоадер, а значение — необходимая идентификация пришедшего COMPLETE, например "вот это он ответил с какого он раёна", "а вот это он ответил почему такой дерзкий".
С коллбеками – если положить их в Dictionary с weakKeys, то даже с повисшим коллбеком не будет проблем.
оффтоп, но будь осторожен http://gskinner.com/blog/archives/2006/07/as3_dictionary_.html
Note that there is a known bug with Dictionary that prevents it from operating correctly with references to methods. It seems that Dictionary does not resolve the method reference properly, and uses the closure object (ie. the “behind the scenes” object that facilitates method closure by maintaining a reference back to the method and its scope) instead of the function as the key. This causes two problems: the reference is immediately available for collection in a weak Dictionary (because while the method is still referenced, the closure object is not), and it can create duplicate entries if you add the same method twice. This can cause some big problems for things like doLater queues.
Zebestov
18.01.2014, 15:41
Оу. Надо же, последний камент от Psycho Tiger невнимательно прочел )) он о том же.
Psycho Tiger
19.01.2014, 12:12
оффтоп, но будь осторожен http://gskinner.com/blog/archives/20...ctionary_.html
Всё в порядке: это если функция = ключ. В моём примере это значение.
Zebestov, ну в конечном счете туда почти любой подход сводится ) Мы больше говорим о том, что городить вокруг него.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.