Архитектурные приемы работы с сокетами

6 просмотров
Перейти к первому непрочитанному сообщению

Илья Плотников

не прочитано,
13 мар. 2010 г., 06:18:3013.03.2010
– ruf...@googlegroups.com
Всем привет.

Хотелось-бы узнать, как кто решает архитектурную проблему работы с
сокетами.
В частности обработку приходящих событий, т.к. они не укладывается в
стандартную модель запрос / ответ.
Хочется раскидать логику обработки событий по классам и при этом не
писать гигантский switch на тысячу строк,
который будет разруливать все это дело, т.к. это становится лакомым
кусочком для вставки костылей.

Заранее благодарю.


Denis Kolyako

не прочитано,
13 мар. 2010 г., 06:29:3613.03.2010
– ruf...@googlegroups.com
> В частности обработку приходящих событий, т.к. они не укладывается в стандартную модель запрос / ответ.

Тут хорошо подходит паттерн Команда.

Денис Коляко
______________________________________________________________________
e...@timezero.ru | http://etcs.ru/ | http://timezero.com/


Илья Плотников

не прочитано,
13 мар. 2010 г., 06:42:1213.03.2010
– ruf...@googlegroups.com
13.03.2010 14:29, Denis Kolyako пишет:

> Тут хорошо подходит паттерн Команда.
>
Основной вопрос куда убрать switch.

switch (event.code) {
case 1001:
break;
case 1002:
break;
case 1003
break;
...
case 1567:
break;
}

Кто будет заниматься этим безобразием и созданием комманд? На ум
приходит только фабрика с методом getCommand(eventCode:int):ICommand;
Возможо есть более элегантные решения.

Denis Kolyako

не прочитано,
13 мар. 2010 г., 06:46:0313.03.2010
– ruf...@googlegroups.com

> Основной вопрос куда убрать switch.

В xml.

Илья Плотников

не прочитано,
13 мар. 2010 г., 06:57:4713.03.2010
– ruf...@googlegroups.com
13.03.2010 14:46, Denis Kolyako пишет:
> В xml.
>
.as .txt .bin Не важно. Как уйти от свича на 1000 строк?

Илья Плотников

не прочитано,
13 мар. 2010 г., 07:00:0313.03.2010
– ruf...@googlegroups.com
13.03.2010 14:46, Denis Kolyako пишет:
>> В xml.
Хотя соглашусь это спасет от лишних костылей в коде.

Denis Kolyako

не прочитано,
13 мар. 2010 г., 07:15:0913.03.2010
– ruf...@googlegroups.com

> .as .txt .bin Не важно. Как уйти от свича на 1000 строк?

Использовать в протоколе имена (вместо id) команд и вызывать как методы.

Ivan Dembicki

не прочитано,
13 мар. 2010 г., 09:34:1613.03.2010
– ruf...@googlegroups.com
Hello Илья,

> .as .txt .bin Не важно. Как уйти от свича на 1000 строк?

Я обычно пользую примерно следующую схему:

DataFormat.as
public static const ATTRIBUTE_COMMAND_ID:String = "id";

public static const START_GAME:String = "1001";
public static const END_GAME:String = "1002";

CommandsHandler.as
private static const handlers:Object = {};

public function registerHandler (commandId:String, handler:Function):Boolean {
if (handlers[commandId]) {
trace("Error: command registered "+commandId);
return false;
}
handlers[commandId] = handler;
return true;
}

public function handleCommand (command:XML):Boolean {
var commandID:String = command.attribute(ATTRIBUTE_COMMAND_ID);
var handler:Function = handlers[commandID] as Function;
if (handler) {
handler(command);
return true;
}
trace("incorrect command id");
return false;
}

Здесь схематично показано, но принцип понятен.

Константы выносятся в отдельный класс, от него наследуется этот класс.
Класс с константами называю обычно что-то вроде DataFormat и он
содержит все именования узлов XML, атрибутов и стандартных значений. И
от него наследуются и другие классы, активно использующие формат.
Этот класс обычно всё-таки портянка, но в нём ничего кроме констант нет.
В продвинутом случае можно сделать не константы (но оставить
именования капсом), а переменные и вначале сессии значения
синхронизировать с сервером.
Как итог, нигде в коде при разборе XML не используются строковые значения.

Все классы, имеющие обработчики команд, регистрируют их с помощью
registerHandler

Обработчик handleCommand может иметь другую логику, разумеется.

Это просто направление куда подумать, всё довольно индивидуально обычно.

--
iv
http://www.bezier.ru
http://bezier.googlecode.com

alexander nemtsov

не прочитано,
13 мар. 2010 г., 15:05:0713.03.2010
– ruf...@googlegroups.com

> Все классы, имеющие обработчики команд, регистрируют их с помощью
> registerHandler
>


И портянка switch..case трансформируется в портянку registerHandler.
Те же яйца, только в профиль.



Александр Немцов
alexande...@gmail.com



Евгений Потапенко

не прочитано,
13 мар. 2010 г., 15:27:5513.03.2010
– ruf...@googlegroups.com
паттерн команда - для простого протокола из максимум десятка типов сообщений.
лучше сделать модель данных на клиенте и на сервере и синхронизировать их между собой неким набором комманд.
во всяком случае я пришел к подобному решению.
хочу в дальнейшем добавить в Realaxy Editor специальный язык для этого.

Ivan Dembicki

не прочитано,
13 мар. 2010 г., 19:58:3513.03.2010
– ruf...@googlegroups.com
Hello Alexander,

>> Все классы, имеющие обработчики команд, регистрируют их с помощью
>> registerHandler
> И портянка switch..case трансформируется в портянку registerHandler. Те же
> яйца, только в профиль.

- боюсь, вы не поняли.
Если есть 300-500 команд, встает серьёзный вопрос. Их никуда не деть.
Как ни крути их останется столько же. И дело даже не в портянке. Если
бы портянка решала свои задачи, то ей были бы только рады.

Вопрос не в том, как сделать из 500 команд 5. Это невозможно и не нужно.
Вопрос в том, как организовать эти 500 команд так, чтобы минимально
заботиться об их обслуживании.
И мое предложение заключается в том, чтобы класс-обработчик независимо
от всего сам регистрировался бы и как-бы напрямую работал с серваком.
И тут помогает proxy.

С точки зрения разработчика:
например, появляется новая команда. Вместо того, чтобы лезть в
гигантский switch или еще чего, разработчик просто создает класс, или
в готовом классе метод, и регистрирует этот метод в CommandsHandler и
всё. Ну, то есть всё. Ничего больше.
Это много?

При этом CommandsHandler понятия не имеет о наличии обработчика. Если
его снести, то никаких ликов не будет. Связанность минимальная.
Получается некий proxy, который просто распределяет поступающую ему
инфу по заданным адресам, которые задекларированы потребателями. При
этом сам понятия не имея кто к нему обращается за услугами.
Не этого ли мы хотим добиться в результате?

Ivan Dembicki

не прочитано,
13 мар. 2010 г., 20:37:5213.03.2010
– ruf...@googlegroups.com
Hello Alexander,

я еще потеоретизирую.

любая команда, это как обычное письмо. Они имеют адрес назначения и
характерное содержимое, предназначенное адресату и никому иному. Если
письмо придёт по ошибке соседу, то он не поймёт какая нафиг баба Нюра
приболела и о какой свадьбе Светочки идет речь. Но правильный
получатель без труда поймёт о чем речь.

И вот у нас стоит задача каким именно образом будет работать почта.
Вариантов масса:

Можно вскрывать все письма, читать их, и принимать решение прямо на
почте должен ли получатель слать подарок на свадьбу Светочке или
попереживать за бабу Нюру.
- Но не замахается ли почта знать так много? Придётся нанять тысячи
сотрудников, чтобы они читали письма, изучали родственные связи
получателей и вместо них принимали решения. Абсурд.

Можно при постройке почты сделать именованые ящики для каждого
получателя. Тогда можно при получении письма быстро его сбрасывать в
соответствующий ящик и дальше пусть сам получатель время от времени
заходит на почту и смотрит нет ли ему письма.
- Но это тоже бред. Если будет построен новый дом, то нужно идти на
почту и устанавливать там новый ящик. Плюс к этому, всем жителям
района придётся постоянно ходить на почту проверять ящик. В итоге,
холостых ходок будет очень много.

Я предлагаю по сути очень простую вещь: почта не имеет никаких ящиков
и понятия не имеет о том, куда доставлять письма, а уж тем более как
их прочесть и отреагировать на них.
Но как только появляется новый дом на районе, его хозяин один раз идёт
на почту и говорит, что если придет письмо на имя Александра Немцова,
то будьте добры его доставить в дом 3 по улице Советских Флэшеров.
При получении он сам его вскроет, прочтет и примет решение как
отреагировать на болезнь бабы Нюры и на свадьбу Светочки.
Каждый занимается своим делом. Почта лишь читает адрес и доставлет
письмо. Получатель решает как реагировать. Если получатель переехал,
ему достаточно уведомить почту. И так далее.
Логично, просто, гибко.

Почта не должна следить за тем, где живёт Александр Немцов - это его
задача сообщить почте свой адрес, если он хочет получать письма.
Почта не должна лезть и читать содержимое письма - не ей оно
предназначено и не ей решать как реагировать на него и что написать в
ответ.
Александр Немцов не должен бегать каждый час на почту проверять свой
ящик - почта должна доставить письмо ему в руки.

Всё просто.

Denis Kolyako

не прочитано,
14 мар. 2010 г., 01:04:3114.03.2010
– ruf...@googlegroups.com
> паттерн команда - для простого протокола из максимум десятка типов сообщений.

Хм, у нас по 300—400.
Никто не говорит о том, чтобы создавать на каждую команду по классу.
Всё добавление команды сводится к написанию соответствующего метода в клиенте соединения
и добавлению описания этой команды в xml протокола.
Никаких левых свитчей, классов и т. п.

alexander nemtsov

не прочитано,
14 мар. 2010 г., 03:12:5914.03.2010
– ruf...@googlegroups.com


Иван, вы все правильно говорите да немного не о том.

Паттерну Коммандный Процессор лет эдак вдвое больше чем флешу, он
безусловно полезен и практичен.
Вы сейчас спорите со мною о том, в чем я с вами итак согласен - о
принципиальной его необходимости.

Командному процессору конечно же нужна таблица "соответствий" между
ключами и выполняемыми командами, путей реализации этой таблицы есть
множество. Денис хранит ее в xml, вы делаете динамическую регистрацию
в рантайме, кто-то пишет портянки свитч-кейсов.

Однако вы утверждаете что от портянки никуда не деться, предлагая ее
"прятать" за динамическое наполнение.
Я считаю что деться от нее можно но мы еще не придумали куда.

Я не имею готового ответа, но изучаю этот вопрос. На этой неделе я
хочу во-первых, поизучать возможность использовать замыкания, во-
вторых, поизучать возможность прикрутить к решению проблемы метатэги.
Можем поизучать вместе :)


>>> Все классы, имеющие обработчики команд, регистрируют их с помощью
>>> registerHandler
>> И портянка switch..case трансформируется в портянку
>> registerHandler. Те же
>> яйца, только в профиль.
>
> - боюсь, вы не поняли.
> Если есть 300-500 команд, встает серьёзный вопрос. Их никуда не деть.
> Как ни крути их останется столько же. И дело даже не в портянке. Если
> бы портянка решала свои задачи, то ей были бы только рады.

Александр Немцов
alexande...@gmail.com



Ivan Dembicki

не прочитано,
14 мар. 2010 г., 03:19:1514.03.2010
– ruf...@googlegroups.com
Hello Denis,

> Использовать в протоколе имена (вместо id) команд и вызывать как методы.

- честно говоря, я бы так не делал.
Любое переименование метода влечет за собой изменение формата данных и
наоборот. Ставить формат данных в зависимость от клиента или от
сервера неверно и усложняет обслуживание кода.

Во вторых, насколько я понимаю, все методы обработчики либо должны
жить в одном классе, либо требуется использование полного пути к
обработчику вида com.domain.game.view.parseCommand. Оба решения
некрасивы.

Ivan Dembicki

не прочитано,
14 мар. 2010 г., 03:27:0914.03.2010
– ruf...@googlegroups.com
Hello Alexander,

> Паттерну Коммандный Процессор лет эдак вдвое больше чем флешу, он безусловно
> полезен и практичен.

- я даже не знал, что такой паттерн есть :)

> Однако вы утверждаете что от портянки никуда не деться, предлагая ее
> "прятать" за динамическое наполнение.

- по-сути да. Портянка образуется на лету в рантайме. Но разве это
плохо? Делается она один раз, много не ест, работает быстро,
обслуживается программистом легко, формат данных можно менять хоть на
лету.
Что еще надо? С чем бороться?

Denis Kolyako

не прочитано,
14 мар. 2010 г., 03:28:3214.03.2010
– ruf...@googlegroups.com

- честно говоря, я бы так не делал.
Любое переименование метода влечет за собой изменение формата данных и
наоборот.
Ставить формат данных в зависимость от клиента или от
сервера неверно и усложняет обслуживание кода.

Вообще говоря, по опыту, метод переименовывается максимум один раз и то,
на этапе разработки.

А быть независимым от формата данных так вообще невозможно.
id или имя — это по барабану, изменение того или другого требует соответствующих действий
и на сервере и на клиенте.
Т. е. принципиально ничего плохого в использовании имен методов вместо их идентификаторов я не вижу.

С тем же успехом можно вызывать методы типа method_N, где N — id команды.
И вот такой варинт как раз отлаживать крайне проблематично.


Во вторых, насколько я понимаю, все методы обработчики либо должны
жить в одном классе, либо требуется использование полного пути к
обработчику вида com.domain.game.view.parseCommand. Оба решения
некрасивы.

Вложенные команды могут решить проблему.

alexander nemtsov

не прочитано,
14 мар. 2010 г., 03:35:5514.03.2010
– ruf...@googlegroups.com

Бороться надо с bad code smells, коими являются любые длинные портянки
- явные или неявные.


>> Однако вы утверждаете что от портянки никуда не деться, предлагая ее
>> "прятать" за динамическое наполнение.
>
> - по-сути да. Портянка образуется на лету в рантайме. Но разве это
> плохо? Делается она один раз, много не ест, работает быстро,
> обслуживается программистом легко, формат данных можно менять хоть на
> лету.
> Что еще надо? С чем бороться?

Александр Немцов
alexande...@gmail.com



Ivan Dembicki

не прочитано,
14 мар. 2010 г., 03:40:2214.03.2010
– ruf...@googlegroups.com
Hello Alexander,

> Бороться надо с bad code smells, коими являются любые длинные портянки -
> явные или неявные.

- с каких это пор динамические коллекции стали пахнуть?
В чём именно заключается дурной запах?

alexander nemtsov

не прочитано,
14 мар. 2010 г., 03:50:2314.03.2010
– ruf...@googlegroups.com

К динамической коллекции как к таковой я претензий не имею.
Я имею претензию к необходимости писать код для регистрации своих
сущностей в этой коллекции. На двести инструкций надо написать двести
регистраций - вот именно этот момент и пахнет.


>> Бороться надо с bad code smells, коими являются любые длинные
>> портянки -
>> явные или неявные.
>
> - с каких это пор динамические коллекции стали пахнуть?
> В чём именно заключается дурной запах?


Александр Немцов
alexande...@gmail.com



Ivan Dembicki

не прочитано,
14 мар. 2010 г., 03:56:5014.03.2010
– ruf...@googlegroups.com
Hello Flexander,

> На двести инструкций надо написать двести регистраций -
> вот именно этот момент и пахнет.

- я бы добавил, что на двести инструкций нужно написать двести обработчиков :)

Нет, ну следуя логике дзен-дизайна, идеальный код - отсутствие кода.
Имеет массу преимуществ: гарантированно нет багов, прост в
производстве и поддержке, понятен при изучении.
Но всё-таки.

Ivan Dembicki

не прочитано,
14 мар. 2010 г., 04:12:4814.03.2010
– ruf...@googlegroups.com
Hello Denis,

> А быть независимым от формата данных так вообще невозможно.
> id или имя — это по барабану, изменение того или другого требует
> соответствующих действий и на сервере и на клиенте.

- в моем варианте хоть на лету можно менять формат. Не структуру
данных конечно, а именования узлов, атрибутов, стандартные значения.
Это и есть достаточная независимость от формата. Максимально возможная
в случае применения XML как транспорта.
Можно выложить файлик с описанием формата и использовать его и
серверному и клиентскому программеру - любой может менять значения
констант без последствий. Его можно вкомпиливать или юзать на лету,
тоже не важно.

> Вложенные команды могут решить проблему.

- есть всего две альтернативы: либо все обработчики держать в одном
классе, либо использовать полный путь.

Первый вариант
- портянка, от которой справедливо мечтает избавиться автор. И похуже свича.

Второй вариант
- засовывание в формат совершенно ненужных данных о путях к классам;
- зависимость кода от формата и наоборот - самый обычный рефакторинг
на клиенте превращается в довольно забавную процедуру с участием
серверных программистов - они будут рады;
- а если и серверные программисты также поступят, то их рефакторинг
станет и твоим;
- лишняя нагрузка на проц за счет необходимости поиска метода по его пути.
- да и вообще не нравится он мне :)

Александр Немцов

не прочитано,
14 мар. 2010 г., 04:16:1414.03.2010
– ruf...@googlegroups.com

А на двести обработчиков надо двести регистраций этих обработчиков :)

Ну согласитесь, если бы необходимости регистрировать не было, то мир
стал бы чуть-чуть -лучше.

Илья Плотников

не прочитано,
14 мар. 2010 г., 04:45:3714.03.2010
– ruf...@googlegroups.com
14.03.2010 11:12, Ivan Dembicki пишет:

> - а если и серверные программисты также поступят, то их рефакторинг
> станет и твоим;
Кстати хороший вопрос как это дело реализовано на серверной стороне. У
них же тоже должна быть конструкция, раскидывающая реквесты клиента.

alexander nemtsov

не прочитано,
14 мар. 2010 г., 04:54:5314.03.2010
– ruf...@googlegroups.com

>
> - есть всего две альтернативы: либо все обработчики держать в одном
> классе, либо использовать полный путь.
>


Вариантов гораздо больше.
Например, смотрите как изящно проблему наполнения карты процессора
решают в Spring ActionScript

[EventHandler(name="saveUser")]
protected function saveTheUserNow(event:Event):void
{
//implementation ommitted...
}

Оппа! Магия! Карта есть, а регистрации нет. Никаких тебе полных путей,
никаких God Class со всезнанием о всех командах etc


Александр Немцов
alexande...@gmail.com



Ivan Dembicki

не прочитано,
14 мар. 2010 г., 04:58:5114.03.2010
– ruf...@googlegroups.com
Hello Alexander,

> Оппа! Магия! Карта есть, а регистрации нет. Никаких тебе полных путей,
> никаких God Class со всезнанием о всех командах etc

- а подписываться на событие не надо?

alexander nemtsov

не прочитано,
14 мар. 2010 г., 05:01:1514.03.2010
– ruf...@googlegroups.com

Судя по документации, нет.

>> Оппа! Магия! Карта есть, а регистрации нет. Никаких тебе полных
>> путей,
>> никаких God Class со всезнанием о всех командах etc
>
> - а подписываться на событие не надо?
>

Александр Немцов
alexande...@gmail.com



Ivan Dembicki

не прочитано,
14 мар. 2010 г., 05:04:2814.03.2010
– ruf...@googlegroups.com
Hello Аlexander,

>> - а подписываться на событие не надо?

> Судя по документации, нет.

- как мило. Он всех вещателей в проекте слушать будет?

Илья Плотников

не прочитано,
14 мар. 2010 г., 05:08:3014.03.2010
– ruf...@googlegroups.com
14.03.2010 11:54, alexander nemtsov пишет:

>
> Оппа! Магия! Карта есть, а регистрации нет. Никаких тебе полных путей,
> никаких God Class со всезнанием о всех командах etc
Нифига не магия. В Spring все конфиги вынесены в xml.
Это то, что предлагал Денис и то, что проблематично использовать в
отсутствии поддержки IDE.
Написание таких xml без автокомплита и проверки целосности классов
приложения в рантайме это ад.

Александр Немцов

не прочитано,
14 мар. 2010 г., 05:11:2614.03.2010
– ruf...@googlegroups.com

Зачем? Там есть "шина" сообщений -достаточно слушать только шину.

Valentin Simonov

не прочитано,
14 мар. 2010 г., 05:32:4414.03.2010
– ruf...@googlegroups.com
На примере Spring Actionscript. Странно, метатэг на protected функции. Вы не
ошиблись?

Последние архитектурные фреймворки (Spring AS, Parsley, Swiz, Mate(?))
активно используют метаданные для самостоятельного разруливания ситуации.

Например, в классе можно объявить такую конструкцию:

[Command("someCommand")]
public function command(id:uint, data:String):void {}

И некоторый сборщик, который "над" всем, подпишет оповещать о пуступающих
командах с именем someCommand эту функцию, при этом будет основываясь на
типах параметров парсить входящие данные и громко ругаться, если что пошло
не так.

По идее вот оно описание: someCommand (id:uint, data:String).

Куда-то прячем еще соответствие id команды и имя команды. Хотябы в тот же
тэг.

О чем говорит Денис, и что реализовано у нас — это по сути то же самое,
только метатэг переносится в XML и типы данных описываются там же.
Получается что-то типа:

<command name="someCommand" id="..">
<param name="id" type="uint"/>
<param name="data" type="String"/>
</command>

А некоторый сборщик, который "над" всем, пытается вызвать публичный метод с
именем someCommand / подписанные на эту команду методы, и передать им
распарсенные данные.

XML хорошо тем, что оно все в одном месте. Еще, можно выдать серверникам и
они на его основе скриптом сгенерят себе код, который посылает/разгребает
все те же команды. Или наоборот. В зависимости кто у вас круче флэшеры или
серверники.


akme

не прочитано,
14 мар. 2010 г., 05:38:5814.03.2010
– ruFlash

Я реализовывал именно на слушателях.

от switch case на обработку сокета уходить не нужно, бо невозможно с
точки зрения логики. Т.к. нельзя подписываться всем контроллерам на
все события.
Каждой комманде с сервера ставится в соответствие Event. Каждый
контроллер подписан только на необходимые ему события. Если необходимо
одно событие обработать двумя контроллерами, причем в определенной
последовательности, то вешается промежуточный слушатель.

В моем случае switch-case можно написать один раз по спецификации с
сервера и благополучно забыть о нем. Даже если вид ответа изменится
потом, то это затронет только контроллер. Events делают свое дело.

Да, и считаю что switch-case существует именно для таких случаев вроде
обработки сокета. А если привязать типизацию сервера к типизации
клиента, как было описано выше, то получится зоопарк и никак иначе,
тут поможет только общий workflow разработки, который затрагивает и
клиент и сервер, но это в разы усложняет систему, что, конечно,
красиво, но эффективно ли?

akme

не прочитано,
14 мар. 2010 г., 05:47:2414.03.2010
– ruFlash
> А некоторый сборщик, который "над" всем, пытается вызвать публичный метод с
> именем someCommand / подписанные на эту команду методы, и передать им
> распарсенные данные.

Вот мне, например, не нравится, что он "пытается" вызвать. Тут должна
быть определенность, вызывает или не вызывает.
Ну и еще связь на мой взгляд слишком жесткой получается.

Maxim Degtyarev

не прочитано,
14 мар. 2010 г., 07:01:2014.03.2010
– alexander nemtsov

Hello alexander,

an> О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫ О©╫О©╫О©╫О©╫.
an> О©╫ О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫ О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫
an> О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫. О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫
an> О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ - О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫.

О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫
О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫.

О©╫ О©╫О©╫О©╫О©╫О©╫О©╫, О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫: О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫
О©╫О©╫О©╫ О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫.

--
Best regards,
Maxim mailto:md...@mail.ru

Daniil Tutubalin

не прочитано,
14 мар. 2010 г., 10:09:5514.03.2010
– ruf...@googlegroups.com
> например, появляется новая команда. Вместо того, чтобы лезть в
> гигантский switch или еще чего, разработчик просто создает класс

> Если его снести, то никаких ликов не будет. Связанность минимальная.

Чтобы этот класс компилировался, его всё-таки придётся упомянуть где-то ещё.
А если его снести, то придётся удалить и в том месте, где упомянули.

Связанность, конечно, всё равно не большая, но она есть.

Ivan Dembicki

не прочитано,
14 мар. 2010 г., 11:03:0014.03.2010
– ruf...@googlegroups.com
Hello Daniil,

> Чтобы этот класс компилировался, его всё-таки придётся упомянуть где-то ещё.
> А если его снести, то придётся удалить и в том месте, где упомянули.
>
> Связанность, конечно, всё равно не большая, но она есть.

- отсюда вывод, что всем классам проекта присуща связанность :)

Daniil Tutubalin

не прочитано,
14 мар. 2010 г., 14:06:0714.03.2010
– ruf...@googlegroups.com
На самом деле это тонкий намёк на фичу в Realaxy Editor, чтоб можно
было сказать: вот в этом пекедже компиляй все классы не зависимо от
того, ссылается на них кто-то или нет.

Daniil Tutubalin

не прочитано,
14 мар. 2010 г., 14:56:3414.03.2010
– ruf...@googlegroups.com
> Я имею претензию к необходимости писать код для регистрации своих сущностей
> в этой коллекции. На двести инструкций надо написать двести регистраций -
> вот именно этот момент и пахнет.

Процесс регистрации кодируется один раз.
Сущности просто вызывают метод: зарегистрируйте меня. Всего одна строчка.

Да, если 200 сущностей, то в сумме будет 200 строчек - но не всей
кучей, а по одной в каждом классе.
И никуда от этого не деться: как-то же мы должны сообщить, что вот
именно этот класс отвечает вот за эту команду.

alexander nemtsov

не прочитано,
14 мар. 2010 г., 15:14:2414.03.2010
– ruf...@googlegroups.com

Предлагаю посмотреть на проблему немного глубже, а не просто
пересчитывать строчки.
Дело не в кол-ве строчек, а в ответственности. Кто-то должен быть
ответственным за регистрацию - кто?
Сам класс по факту инстансирования? Некий глобальный мегаменеджер? А
какие функции он выполняет кроме как регистрирует? А нужен ли такая
бизнес-единица, назначение которой "бумажки с места на место
перекладывать"?

Мне очень нравится идея с метатэгами. Единственное что утрачивается в
таком подходе - нет централизованного хранилища всех доступных к
исполнению инструкций. А нужно ли оно, централизованное?
Александр Немцов
alexande...@gmail.com



Daniil Tutubalin

не прочитано,
14 мар. 2010 г., 16:16:1714.03.2010
– ruf...@googlegroups.com
Кастомные метатеги сами по себе никакого функционала не несут.
Значит в любом случае кто-то должен просканировать метатеги и всё-таки
где-то зарегистрировать, мол, вот такой класс/метод, согласно
предъявленной справке отвечает теперь за такую-то команду.

По поводу того, кто должен отвечать за регистрацию: тут вопрос
спорный. И в том и в другом случае есть свои плюсы и минусы.
Но централизованное хранилище будет в любом случае, ведь, как я уже
говорил, если класс нигде не упомянут, то компилятор его и не
учитывает.
А значит таки где-то придётся упомянуть все классы, ответственные за
обработку команд.

Ivan Dembicki

не прочитано,
14 мар. 2010 г., 16:41:1714.03.2010
– ruf...@googlegroups.com
Hello alexander,

> Дело не в кол-ве строчек, а в ответственности. Кто-то должен быть
> ответственным за регистрацию - кто?

- я выше раписывал пример про почту.
Сообщить почте где ты живешь - твоя обязанность.


> А нужен ли такая бизнес-единица, назначение которой "бумажки с места на место перекладывать"?

- нужна. Альтернатива - только создание индивидуального сокет
соединения каждым обработчиком.
Но это и накладно и невозможно и, даже если бы было возможно, то
чревато ошибками - в таком раскладе возможно изменение
последовательности прихода команд.

makc

не прочитано,
14 мар. 2010 г., 20:10:4114.03.2010
– ruFlash
-X-No-Archive: yes

On Mar 14, 9:56 am, Ivan Dembicki <ivan.dembi...@gmail.com> wrote:
> следуя логике дзен-дизайна, идеальный код - отсутствие кода.
> Имеет массу преимуществ: гарантированно нет багов, прост в
> производстве и поддержке, понятен при изучении.

в цитатник.

Максим Буньков

не прочитано,
14 мар. 2010 г., 21:49:3314.03.2010
– ruf...@googlegroups.com
Ребят из всей этой дребедени я понял, что вы хотите доставлять почту без регистрации адресов получателей в почтовой службе?

alexander nemtsov

не прочитано,
15 мар. 2010 г., 01:10:1115.03.2010
– ruf...@googlegroups.com
[оффтопик]
Сообщать почте я ничего не должен - почта доставляет посылку по
адресу. Выдачей адресов занимается БТИ :) Собственно, примерно так оно
и обстоит с метатэгами.

Уж если продолжать аналогию с почтой - почта ведь тоже бывает
разной :) Бывает, к примеру, наша родная почта, которая посылки из
Китая в Хабаровск (буквально через дорогу то есть) возит через Москву
(то есть через гигантский мегаменеджер).

Плюсы - централизация и... эээ... все, плюсы кончились.
Минусы - чумовое время доставки и нереально завышеная стоимость
транспортировки. Бешеный бюрократический аппарат съедает всю ту
экономию, которую удалось достичь через увеличение объемов
операционной деятельности.
[/оффтопик]

>> Дело не в кол-ве строчек, а в ответственности. Кто-то должен быть
>> ответственным за регистрацию - кто?
>
> - я выше раписывал пример про почту.
> Сообщить почте где ты живешь - твоя обязанность.

Александр Немцов
alexande...@gmail.com



Андрей Скорик

не прочитано,
15 мар. 2010 г., 04:35:5015.03.2010
– ruf...@googlegroups.com
по поводу Mate
да там есть два типа карт событий глобальные, которые слушают весь
дисплей и локальные, которым можно указать в качестве диспатчера,
конкретный вид/компонент.

Из практики у меня сложилась примерно такая схема:
глобальная карта выполняет сл. функции:
1) инстанцирует мегаменеджера и рутовую модель :) - но "мега" в
хорошем смысле, он рулит только ситуации касающиеся всего приложения в
целом и пишет результаты своих действий в рутовую модель
2) весь обмен с сервером лежит в ней (ибо удобно)
3) получает события уровня приложения, вызывает методы мегаменеджера
для их обработки

инъекторы зависимостей обычно выношу в отдельную карту - так их проще
обслуживать

каждый вид (достаточно обособленная визуальная часть приложения)
инъектируется собственным менеджером и моделью (которая связана с
рутовой моделью через инжекторы) - чтобы в самом визуальном компоненте
ничего кроме собственно визуальной части и интерактива не было (так
проще модифицировать приложение).. т.е. здесь что-то вроде
Presentation Model
вид знает о менеджере и вызывает его публичные методы, менеджер не
знает о виде ничего.

В виду темы обсуждения - скорее всего был бы сделан один обработчик
сокетного события типа ServerRequestEvent.IncomingData и второй для
ServerRequestEvent.OutgoingData
по ним бы дергались соответствующие методы менеджера приложения -
который бы внутри имел инстанс менеджера соединения и :)) таблица
соответствий типов сообщений (портянка) размещалась бы в нем или во
внешнем файле, который бы грузился или компилился не принципиально..

всем остальным как бы до этого дела вообще нет.. пришли данные,
обработались соотв. манагером и сложились в модель - из модели кому
интересно, те их и растащили..

получили пользовательский ввод - обработали на уровне вида и его
адаптера - записали в собственную модель, приняли решение о
необходимости оповестить остальное приложение - отдиспатчили соотв.
событие. карта поймала - дернула менеджера приложения - тот принял
решение по ситуации, дернул RPC и/или сложил данные в модель.

Круг замкнулся.

--
С уважением, Скорик Андрей. andrew...@gmail.com

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 10:56:0115.03.2010
– ruf...@googlegroups.com
Hello Alexander

> [оффтопик]
> Сообщать почте я ничего не должен - почта доставляет посылку по адресу.

- это совковая система. В нормальной системе достаточно прийти на
почту и попросить всю корреспонденцию на мое имя пересылать по новому
адресу.
Если такой системы нет, то после переезда письма будет получать не тот
адресат или они забьют прочтовый ящик под завязку.

> Выдачей адресов занимается БТИ :) Собственно, примерно так оно и обстоит с
> метатэгами.

- ну давай так: в итоге, мы ведь всё равно получаем swf и там всё это
проходим, причем не всегда понятно и управляемо.

> почта, которая посылки из Китая в Хабаровск [...] возит через Москву (то есть через
> гигантский мегаменеджер).

- не совсем так. В моем предложении, опять же по аналогии, есть один
таможенный пункт с Китаем, и тут же сортировочная почты. Каждый
получатель сообщает свой адрес и напрямую оттуда получает
корреспонденцию из китая.

> Плюсы - централизация и... эээ... все, плюсы кончились.

- о какой централизации идёт речь?
давай так:

Вариант 1.
каждому классу создается отдельное сокетное соединение. Децентрализация полная.
Итог: 400-500 сокет-соединений (допустим, что это возможно). Каждое
соединение нужно создать, обслуживать, обрабатывать ошибки, закрывать,
и т.п.
В аналоги с почтой - каждому получателю вменяется в обязанность
таскать с собой почтовое отделение. Нужно ему это? Нму почта нужна, а
не почтовое отделение.

Вариант 2.
создается одно сокетное соединение. Дальше как ни крути, можно
сказать, что почта приходит в страну централизованно. Вопрос лишь в
том, сколько этапов потребуется для того, чтобы доставить почту
получателю.

а) в моем варианте каждый получатель сообщает этому центру где он
живет и всё. Если адрес сменился, то получатель сообщает это на почту.
В итоге, отправителю достаточно указать имя получателя. Дальше, как
только почта пришла, получатель немедленно и без дополнительных
посредников ее получает. Метод: либо через подписку на событие, либо
через регистрацию обработчика.
Итог:
при создании получателя требуется уведомить почту о себе. При смене
адреса не требуется.

б) можно раздать всем адреса. Отправитель, помимо имени получателя
должен знать где живёт получатель. Если вдруг изменится адрес, то пока
отправитель не узнает новый адрес, почта будет ошибочно доставлять
корреспонденцию. Также несколько увеличивается нагрузка на почту,
поскольку она должна вычислить адрес.
Итог:
при создании или изменении адреса получателя требуется уведомить
отправителя в Китае.

в) можно собрать представителей получателя на почте, раздавать им
почту и пусть они сами заботятся как ее доставить получателю. Но будут
ли это получатели? Нет. Это будут посредники. Их будет много и они все
будут толкаться на почте.
Реализация: switch или множество обработчиков, одноименных с командой.
Итог:
при создании получателя требуется создание посредника. Почта будет
приходить через посредника.


> Минусы - чумовое время доставки и нереально завышеная стоимость

- давай пройдём по цепочке любого варианта и ты увидишь, что
предложенный мною вариант быстрее всех.

Илья Плотников

не прочитано,
15 мар. 2010 г., 10:59:3915.03.2010
– ruf...@googlegroups.com
15.03.2010 11:35, Андрей Скорик пишет:
> ... и :)) таблица

> соответствий типов сообщений (портянка) размещалась бы в нем или во
> внешнем файле, который бы грузился или компилился не принципиально...
>
Видимо никуда не деть эту портянку - реестр.
Либо она будет явно описана в определенном файле, либо будет скрыта за
матаданными и собираться в рантайме.
Спросил у серверных программистов - на их стороне все тоже самое с
событиями, приходящими от клиента.

На мой взгляд не стоит прятать логику приложения ни за метадатой, ни
выносить ее во внешние файлы.
Этим мы лишаемся родной проверки компилятора. Даже если представить, что
мы пишет плагины к IDEA / Eclipse, которые
будут в курсе предназначения наших xml или java тулзы вместе с build
скриптами для проверки целосности метадаты,
мы получаем кучу геморроя с разработкой и поддержкой этих решений в будущем.

Собственно у меня вопросов больше нет. Всем спасибо.

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 11:17:5515.03.2010
– ruf...@googlegroups.com
Hello Alexander,


> Плюсы - централизация и... эээ... все, плюсы кончились.

- низкая стоимость разработки:
требуется только один центр приема и обработки данных, ошибок,
подключений и т.п.

- низкая стоимость смены транспорта:
при переходе от XML к другому формату или при изменении формата меняем
код только в одном месте.

- высокая скорость доставки:
после попадания данных в ролик только один посредник. Меньше
невозможно технически - количество сокет соединений ограничено.

И еще раз:
на выхлопе мы в любом случае имеем swf. Так или иначе, метатеги там
или подписка, всё равно в итоге будет хоть один посредник - тот, кто
сидит на сокете и раздает дальше.
В моем варианте он отдает напрямую, без создания новых сущностей.
В варианте с метатегами я не уверен, скорее всего будет еще один
посредник как минимум.

Регистрация в моем варианте - стандартный код, хорошо поддерживаемый
редакторами - одна строчка. Есть возможность отписки на лету.

Метатеги - нестандартная вешь, плохо поддерживаемая редакторами. И
кстати, тоже одна строчка, как ни крути. На лету не отпишешься. В
итоге просто изврат ради изврата. Чтобы все кругом знали, что мсье
знает толк в извращениях.

Valentin Simonov

не прочитано,
15 мар. 2010 г., 11:31:4215.03.2010
– ruf...@googlegroups.com
Ну уж не скажи.
Реализация IoC через метатэги очень вкусная.
Позволяет избежать толпы ненужного кода и кучи тупых проблем.
Нужно всего лишь один раз написать/взять готовый большой класс для работы с
ними.

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 11:50:4515.03.2010
– ruf...@googlegroups.com
Hello Valentin,

> Реализация IoC через метатэги очень вкусная.
> Позволяет избежать толпы ненужного кода и кучи тупых проблем.
> Нужно всего лишь один раз написать/взять готовый большой класс для работы с
> ними.

Вот смотри:
[EventHandler(name="saveUser")]

Скажзи плз, какой редактор даст автокомплит хоть на одном из слов?
Какой редактор проверит правильность написания этих слов?

Честно говоря, я не очень понимаю о чем речь (а это тоже минус
технологии - усложнение, удорожание), не знаю кто ее поддерживает и
какие подводные грабли меня там ждут.

Возможно где-то их применение оправдано. Но не в этой теме точно.

makc

не прочитано,
15 мар. 2010 г., 12:31:4615.03.2010
– ruFlash

On Mar 15, 5:50 pm, Ivan Dembicki <ivan.dembi...@gmail.com> wrote:
> Честно говоря, я не очень понимаю о чем речь (а это тоже минус
> технологии - усложнение, удорожание), не знаю кто ее поддерживает и
> какие подводные грабли меня там ждут.

можно ж и погуглить, например http://blog.gtuhl.com/2007/03/05/flex2-custom-metadata/

kuril

не прочитано,
15 мар. 2010 г., 12:43:5815.03.2010
– ruFlash

> а) в моем варианте каждый получатель сообщает этому центру где он
> живет и всё. Если адрес сменился, то получатель сообщает это на почту.
> В итоге, отправителю достаточно указать имя получателя. Дальше, как
> только почта пришла, получатель немедленно и без дополнительных
> посредников ее получает.

Если адрес меняется получатель обычно сообщает отправителям, а на
почту только в том случае, когда проблематично уведомить всех
отправителей. Почта не работает с именами, потому что адрес как путь к
файлу, позволяет письму сначала отправиться в город, потом в
конкретное отделение по индексу а потом уже ищется конкретный адресат.

> б) можно раздать всем адреса. Отправитель, помимо имени получателя
> должен знать где живёт получатель. Если вдруг изменится адрес, то пока
> отправитель не узнает новый адрес, почта будет ошибочно доставлять
> корреспонденцию.

Отправитель всегда должен знать адрес получателя! В обычной, улиточной
почте имя получателя рассматриватся как часть адреса, и когда вы
вынимаете письмо из ящика, вы свичом перебираете, кто тут из нас Иван
Иваныч, или, если письмо требует личного вручения адресату, это делает
почтальон, а при смене адреса вы само собой сообщаете об этом, но я не
могу представить себе необходимость смены адреса в рантайме, куда
обработчикам нужно переезжать за время работы приложения?

> в) можно собрать представителей получателя на почте, раздавать им
> почту и пусть они сами заботятся как ее доставить получателю. Но будут
> ли это получатели? Нет. Это будут посредники. Их будет много и они все
> будут толкаться на почте.

Это опять совковый подход, посредник это не тот который только
мешается, а которому делегируются некие функции дабы я не
заморачивался с почтовыми конвертами, мне интересно только содержимое
письма, и я не хочу знать как надо распаковывать/запаковывать конверты
и вообще как работать с почтой, я просто прошу посредника прочитать
мне письмо и диктую ответ. А если посредник может работать с
несколькими клиентами, то тогда их на почте будет толкаться всяк
меньше, чем реальных получателей.

> - давай пройдём по цепочке любого варианта и ты увидишь, что
> предложенный мною вариант быстрее всех.

Быстрее что? Обработка пакета, регистрация адресов, или разработка?
Для более менее сложной системы обменов данными небходим протокол
обмена. Еще где-то выше было сказано, удобное решение это
синхронизация модели данных сервера и клиента, и серилизация этих
данных, чтобы не гонять по сети длинные строки с именами комманд.
Портянки с адресами сматываются в айдишники которые разбиваются по
маске [сервис][служба][комманда] Пакет состоит из объекта данных и
заголовка, в заголовке по мимо прочего пишется адрес и пока объект не
дойдет до комманды обработчика, он никак не обрабатывается читается
только заголовок, конверт с адресом, и по нему письмо сначала попадает
в сервис оттуда в службу и до конкретной комманды доходит уже
распечатаный объект. С первого взгляда может показаться слишком сложно
и хочется побыстрее напрямую доставить, ну так ничто не мешает
разместить все в одном, просто когда портянка вырастает мы ее легко
сможем разделить на отдельные сервисы, которые уже можно
регистрировать динамически.

kuril

не прочитано,
15 мар. 2010 г., 12:46:4915.03.2010
– ruFlash

> Вот смотри:
> [EventHandler(name="saveUser")]
>
> Скажзи плз, какой редактор даст автокомплит хоть на одном из слов?
> Какой редактор проверит правильность написания этих слов?

IDEA даст сразу на двух)

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 13:04:2815.03.2010
– ruf...@googlegroups.com
Hello makc,

> можно ж и погуглить, например http://blog.gtuhl.com/2007/03/05/flex2-custom-metadata/

- поверь, я при необходимости, могу написать такую же статью про метедату.
Дело то не в том, что я должен знать как работать с метаданными.
Дело в том, что я должен изучить чьё-то решение, основанное на метаданных.
Плюс заморочиться с тем, чтобы эти метаданные не потерялись при компиляции.

А погуглить можно и на тему Большого Взрыва. Но не факт, что после
этого ты сможешь назвать себя физиком.

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 13:05:4715.03.2010
– ruf...@googlegroups.com
Hello Kuril,

> IDEA даст сразу на двух)

- на двух стандартных.
На пользовательских не даст.

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 13:53:4615.03.2010
– ruf...@googlegroups.com
Hello kuril,

> Если адрес меняется получатель обычно сообщает отправителям, а на
> почту только в том случае, когда проблематично уведомить всех
> отправителей.

- а представь, что ты вообще не знаешь, кто отправитель.
Плохая почта письмо потеряет. Хорошая доставит.
Мы ведь хорошую почту хотим, правда?

> Почта не работает с именами [...]
- Плохая почта не работает с именами. У хорошей нет проблем.
А мы можем сделать хорошую. Зачем копировать поведение плохой?
Мы ведь хорошую почту хотим, правда?

> Отправитель всегда должен знать адрес получателя!

- весьма спорное утверждение. Даже на советской почте можно сделать
абонентский ящик и получать письма на него. И там же дать распоряжение
о том куда им сообщать, если что-то пришло. Если это реально сделать у
нас, то почему бы не сделать?
Мы ведь хорошую почту хотим, правда?


> я не могу представить себе необходимость смены адреса в рантайме, куда
> обработчикам нужно переезжать за время работы приложения?

- вышел игрок из мультипользовательской игры - убили экземпляр класса.
Зашел - создали. И фантазию даже не особо пришлось напрягать.

> я не хочу знать как надо распаковывать/запаковывать конверты
> и вообще как работать с почтой, я просто прошу посредника прочитать
> мне письмо и диктую ответ.

- так и я о чем. Мы делаем систему. Так или иначе в этой системе
должны быть расписаны все обязанности. Вопрос лишь в том, чтобы не
насоздавать лишних посредников и где правильно расположить тот или
иной функционал.
Нефига тащить почту каждому в дом. Но и почта не вправе читать мои письма.

> А если посредник может работать с
> несколькими клиентами, то тогда их на почте будет толкаться всяк
> меньше, чем реальных получателей.

- да лишние они и всё тут. Даже один - уже много.


> Быстрее что? Обработка пакета, регистрация адресов, или разработка?

- всё.

> Для более менее сложной системы обменов данными небходим протокол
> обмена. Еще где-то выше было сказано, удобное решение это

> синхронизация модели данных сервера и клиента [...]

- кто бы был против.
Но, во первых мы обсуждаем не протоколы данных, а систему приема
данных при готовом протоколе. К тому же, не факт, что:
а) протокол вообще можно поменять
б) другой протокол будет лучше отвечать требованиям проекта.


> Портянки с адресами сматываются [...]

- да, красиво. Но не всем походит.
Выбор протокола - разговор отдельный и там свои требования.

kuril

не прочитано,
15 мар. 2010 г., 15:06:0015.03.2010
– ruFlash

On Mar 15, 8:05 pm, Ivan Dembicki <ivan.dembi...@gmail.com> wrote:
> Hello Kuril,
>
> > IDEA даст сразу на двух)
>
> - на двух стандартных.
> На пользовательских не даст.

и что, такая великая проблема сделать пользовательский? Просто не
занимался таким вопросом, но уверен, что если есть вообще такая
поддержка, то расширить ее можно!

kuril

не прочитано,
15 мар. 2010 г., 15:06:1115.03.2010
– ruFlash

> > Почта не работает с именами [...]
>
> - Плохая почта не работает с именами. У хорошей нет проблем.
> А мы можем сделать хорошую. Зачем копировать поведение плохой?
> Мы ведь хорошую почту хотим, правда?

Пример хорошей почты, которая работает с большим количеством имен
есть? На ум приходит только DNS, но это дополнительная сущность, но мы
ведь не хотим плодить сущности правда? Тем более там имя тоже нельзя
создать так просто, само имя должно разбиваться на части.

> > Отправитель всегда должен знать адрес получателя!
>
> - весьма спорное утверждение. Даже на советской почте можно сделать
> абонентский ящик и получать письма на него. И там же дать распоряжение
> о том куда им сообщать, если что-то пришло. Если это реально сделать у
> нас, то почему бы не сделать?
> Мы ведь хорошую почту хотим, правда?

Извините, абонентский ящик это и есть адрес, только конечный адресат
обозначен как номер а не имя фамилия, в чем спорность утверждения?

> > я не могу представить себе необходимость смены адреса в рантайме, куда
> > обработчикам нужно переезжать за время работы приложения?
>
> - вышел игрок из мультипользовательской игры - убили экземпляр класса.
> Зашел - создали. И фантазию даже не особо пришлось напрягать.

Если на каждый вход/выход игрока регистрируется/удаляется что-то типа
комманды, то это плохо спроектированная мультипользовательская игра!

> > я не хочу знать как надо распаковывать/запаковывать конверты
> > и вообще как работать с почтой, я просто прошу посредника прочитать
> > мне письмо и диктую ответ.
>
> - так и я о чем. Мы делаем систему. Так или иначе в этой системе
> должны быть расписаны все обязанности. Вопрос лишь в том, чтобы не
> насоздавать лишних посредников и где правильно расположить тот или
> иной функционал.
> Нефига тащить почту каждому в дом. Но и почта не вправе читать мои письма.

Так и я о чем, посредник просто не в состоянии прочитать письмо,
потому что он умеет читать только адреса, а вот я не хочу учиться
читать адреса, потому что моя задача сосредоточиться на содержимом
письма. Если в вашей системе логически возникает новая сущность, то
глупо игнорировать ее и прятать в другой сущности, с которой она
только связана, но сама ей не является.

> > Быстрее что? Обработка пакета, регистрация адресов, или разработка?
>
> - всё.

Чтобы было всего много вкусно и бесплатно так не бывает, по закону
сохранения энергии, если где-то прибудет обязательно где-то убудет,
надо это иметь ввиду и расставлять приоритеты!

> > Для более менее сложной системы обменов данными небходим протокол
> > обмена. Еще где-то выше было сказано, удобное решение это
> > синхронизация модели данных сервера и клиента [...]
>
> - кто бы был против.
> Но, во первых мы обсуждаем не протоколы данных, а систему приема
> данных при готовом протоколе. К тому же, не факт, что:
> а) протокол вообще можно поменять
> б) другой протокол будет лучше отвечать требованиям проекта.
>
> > Портянки с адресами сматываются [...]
>
> - да, красиво. Но не всем походит.
> Выбор протокола - разговор отдельный и там свои требования.

Где там у сокета готовый протокол? TCP обеспечивает только доставку
байтов и все, тут нужен новый прикладной уровень, и разбор байт
массива будет в любом случае протокол, просто когда надо отправить
пару xml об этом не задумываешься и твой протокол примитивен, а когда
разнообразие этих xml увеличивается, возникают такие вопросы. Если
сделать сделать синхронизацию данных, когда сервер посылает объект и
клиент всегда знает как этот объект читать, то есть получает объект
as, то написание протокола сильно упрощается!

Ivan Dembicki

не прочитано,
15 мар. 2010 г., 16:20:1515.03.2010
– ruf...@googlegroups.com
Hello Kuril,

> Пример хорошей почты, которая работает с большим количеством имен есть?

- выше я предложил вариант, который прекрасно работает на заданных 400
командах.
Единственный вопрос - уникализация имен команд. Здесь он не стоит.
Когда нас попросят запрограммировать Интернет2, тогда и будем думать о
том, как разрулить.

> Извините, абонентский ящик это и есть адрес, только конечный адресат
> обозначен как номер а не имя фамилия, в чем спорность утверждения?

- в том, что я говорю, что достаточно иметь имя команды вида "foo",
чтобы вызвать метод fooHandler в классе
com.domain.project.folder.class, а не знать полный путь к нему вида
"com.domain.project.folder.class.fooHandler", как предлагалось Денисом
Коляко выше.

> Если на каждый вход/выход игрока регистрируется/удаляется что-то типа
> комманды, то это плохо спроектированная мультипользовательская игра!

- не нам решать. Это зависит исключительно требований проекта, его
особенностей. В целом ряде случаев это может быть оправдано и
эффективно.
И опять-же это лишь возможность, а не требование. Возможность всегда
лучше иметь, чем не иметь.

> Если в вашей системе логически возникает новая сущность, то
> глупо игнорировать ее и прятать в другой сущности, с которой она
> только связана, но сама ей не является.

- да слава богу. На самом деле это лишь вопрос кого ты считаешь
получателем - какую-нибудь вьюху или модель данных. Твое право
выделить хоть специального читателя - мое предложение никак не
регламентирует это и оставляет право архитектору приложения самому это
решить.


> Чтобы было всего много вкусно и бесплатно так не бывает

- бывает. Если тупо, то медленно и дорого. Если по уму, то быстро и дешево.


> Где там у сокета готовый протокол? TCP обеспечивает только доставку [...]

- вопрос в первом письме прочти плз. Мы вроде как его обсуждаем.
Если интересно пообщаться на тему форматов, давай создадим новый топик.

k0t0vich

не прочитано,
16 мар. 2010 г., 04:43:4516.03.2010
– ruFlash
я делал через команды так:
http://www.flasher.ru/forum/blog.php?b=72
сериализация/десериализация в любом случае нужна - у меня псевдонимы
регистрировались в фасаде приложения через
registerClassAlias("py.Person", Person);
после прихода и сериализации сообщения ( являющихся по-сути командами
контроллера) у всех команд вызывается метод
changeModel(model)

Daniil Tutubalin

не прочитано,
16 мар. 2010 г., 04:51:4616.03.2010
– ruf...@googlegroups.com
У всех команд? На любое изменение?

k0t0vich

не прочитано,
16 мар. 2010 г., 05:07:5516.03.2010
– ruFlash
> У всех команд? На любое изменение?
Да, а почему нет?
Есть проект где порядка 200 сообщений. Команды, кроме, собственно,
данных сообщения - на стороне клиента содержат логику контроллера. Да
- это децентрализация логики контроллера или если смотреть с плохой
стороны - размазывание логики, но с хорошей стороны такая схема
позволяет легко добавлять новые команды любой сложности.
Само собой, что вся структура приложения должна быть строго MVC. т.е.
изменение Видов зависят только от изменения модели.

Андрей Скорик

не прочитано,
16 мар. 2010 г., 05:42:3716.03.2010
– ruf...@googlegroups.com
> Само собой, что вся структура приложения должна быть строго MVC. т.е.
> изменение Видов зависят только от изменения модели.

эта зависимость тоже должна быть минимизирована. что достигается
написанием адаптера для вида. т.е. его собственной модели, которая
преобразует данные модели приложения в тот формат который понятен и
удобен для конкретного вида/компонента.

тогда один и тот же один раз сделанный визаульный компонент может быть
использован в разных версиях приложения/приложениях с разными
моделями. задача переноса сводится к модификации/написанию адаптера

k0t0vich

не прочитано,
16 мар. 2010 г., 05:43:4716.03.2010
– ruFlash
т.е. по сути - сервер является контроллером - который изменяет данные
модели (через посредников в виде клиентских классов IMessage)
отправляя класс QuitMessage{} - изменяет в модели статус игры на quit
или ChangeStatusMessage {status:"quit"} - аналогично QuitMessage
ChangeCharacterPostion{id:11121,x:100,y:100} - сменяет модель
персонажа с id 11121. Причём большим плюсом protobuf является
необязательность передачи ВСЕХ параметров: так, передав
ChangeCharacterPostion{id:11121, x:100} мы сменяем только позицию x.

Daniil Tutubalin

не прочитано,
16 мар. 2010 г., 05:58:2316.03.2010
– ruf...@googlegroups.com
Зачем всем командам знать, что персонаж переместился?
Просто ради того, чтобы каждая команда могла лично убедиться, что ей
это сообщение не интересно?

k0t0vich

не прочитано,
16 мар. 2010 г., 06:46:3316.03.2010
– ruFlash
> Зачем всем командам знать, что персонаж переместился?
всё не так.
с сервера пришёл набор байт.
socketHandler сериализовал этот набор байтв новый экземпляр класса
message = ChangeCharacterPostion, у которого x=100, id=11121
затем вызов message.changeModel(model)
в классе ChangeCharacterPostion - > changeModel изменяет
model.chracterModelList[this.id].x=this.x;

Dimarik Valeev

не прочитано,
16 мар. 2010 г., 06:56:5016.03.2010
– ruFlash

On 16 мар, 12:43, k0t0vich <b_konstan...@list.ru> wrote:
> т.е. по сути - сервер является контроллером - который изменяет данные
> модели

Я предлагаю рассмотреть вариант, в котором сервер служит частным
случаем источника данных. Подмените/дополните питание приложения еще
каким-либо источником данных - файл, LocalConnection,
ExternalInterface, flashvars, пользовательский ввод - суть от этого не
изменится. А контроллер, который в MVC, непосредственно воздействует
на модель, анализируя входные поток(и) данных. В конце концов никто не
запрещает контроллеру вообще игнорировать какой-либо источник данных в
своей работе.

Т. о. я бы уточнил, что сервер является контроллером лишь
опосредованно.

Яски

не прочитано,
17 мар. 2010 г., 00:24:4017.03.2010
– ruFlash
У варианта Ивана есть серьезный минус -- зная сигнатуру команды очень
сложно найти место в коде, которое ее обрабатывает. Также непонятно
что делать, когда нет обработчиков у команды или их несколько. Мне
кажется, здесь лучшим вариантом будет описание формата в xml +
кодогенерация клиентских и серверных классов для посылки/получения
команд из него.

Ivan Dembicki

не прочитано,
18 мар. 2010 г., 15:54:4718.03.2010
– ruf...@googlegroups.com
Hello Яски,

> У варианта Ивана есть серьезный минус -- зная сигнатуру команды очень
> сложно найти место в коде, которое ее обрабатывает.

- этот недостаток есть только при условии, если используются строковые
данные при регистрации.
В моем предложении есть четкое указание на необходимость использования
класса с константами.
В этом случае достаточно открыть этот класс, найти в нем нужную
команду и посмотреть где была использована константа (Ctrl+R)

> Также непонятно
> что делать, когда нет обработчиков у команды или их несколько.

- ну, всё зависит от стратегии.
Я бы не разрешал наличие двух обработчиков, а при отсутствии
обработчика кидал бы дебаг сообщение.

Яски

не прочитано,
19 мар. 2010 г., 17:07:1419.03.2010
– ruFlash
>>В моем предложении есть четкое указание на необходимость использования
>>класса с константами.
Не будет работать. Потому что написание кода оно сейчас, а константы
могут понадобиться в некотором будущем, на их написание будут
забивать.
Чтобы решить эту проблему нужно бороться с причиной такой проблемы --
приходящее сообщение должно напрямую попадать в обработчик. Но чтобы
приложение не стало слишком громоздким нужно группировать обработчики
по функциональности. Например, имеем большую сложную игру -- у нас есть
гуи, различные индикаторы, игровые персонажи, сетевое время. Это будет
выглядеть примерно так:
OnSocketData(data) {
if (data.id < 100) {
gui.ProcessMessage(data);
} else if (data.id < 200) {
led.ProcessMessage(data);
} else if (data.id < 300) {
game.ProcessMessage(data);
} else if (data.id == 300) {
time.ProcessMessage(data);
}
}
Каждый из высокоуровневых обработчиков знает как обработать или кому
передать сообщение дальше.
У этого способа минус в большом количестве одинакового кода -- логика
почтальона разнесена по многим классам и если изменится формат
сообщения это повлечет большие переделки.
Поэтому я предлагаю хранить описание формата -- ту часть, которая
меняется несильно в xml, а функциональность почтальона генерировать из
этого описания.

Ivan Dembicki

не прочитано,
20 мар. 2010 г., 00:41:3820.03.2010
– ruf...@googlegroups.com
Hello Яски,

> Не будет работать. Потому что написание кода оно сейчас, а константы
> могут понадобиться в некотором будущем, на их написание будут
> забивать.

- ну, если на такие вещи забивать, то нет смысла предлагать что-то,
потому, что на что угодно можно забить.


> Но чтобы приложение не стало слишком громоздким нужно группировать обработчики

- да, вполне реально и разнести на группы. В определенных ситуациях
это будет полезно.

Flop Serg

не прочитано,
21 мар. 2010 г., 17:43:2321.03.2010
– ruf...@googlegroups.com
> - да, вполне реально и разнести на группы. В определенных ситуациях
> это будет полезно.

можно вообще пойти по бинарному дереву и для каждого бита свой иф )

а вообще если много-много чего нужно обрабатывать то тут нужно не
архитектуру придумывать.
все архитектурные решения уже придумали до нас.

тут важное автоматизировать процесс:

например:
вам нужна новая комманда от сервера к клиенту или от клиента к серверу
- вы ее вписываете в ваш документ это может быть просто xls
- она парсится какми-то образом, пусть даже скриптом на перле
- этим парсером генерятся серверные и клиентские классы наследники
вашей системмы
- так-же перегенеривается портянка соответствий (в хмл, динамичная или
свиче статичная - неважно)
-- по вкусу можно еще клиентский и серверный код задокументировать и сбилдить.


по итогу вы пишите в доке:
myMegaCommand(param1:int,param2:int,param3:String):int | это сервер
просит клиенета сложить два числа param1 и param2, param3 просто
строка
и у вас сразу две новые либы на клиенте swc и на сервере jar

а программисты видят эти изменения, апдейтятся и могут сразу
на сервере:

IServerCommand command = new myMegaCommand(5,10,"asdsad");
command.assignResponceHandler(myMegaCommandHandler);
command.execute();

на клиенте:
SocketClient.instance.addEventListener(SocetClientCommands.MY_MEGA_COMMAND,
onMyMegaCommand); // MY_MEGA_COMMAND - константа сгенерена скриптом

private function onMyMegaCommand(e:onMyMegaCommandEvent):void { //
onMyMegaCommandEvent - класс сгенереный скриптом
trace(e.id, e.param3); // e.id - нужно продумать уникальность, чтобы
можно было отличить две одновременные одинаковые комманды
var responce:IClientResponce = new MyMegaCommandResponse(e.id,
e.param1 + e.param2) // MyMegaCommandResponse - сгенеренный класс
SocketClient.instance.processPesponce(responce)
}

А еще передавать в запросах клиент-сервер нужно ведь и сложные типы -
их тоже нужно автоматом генерить.. и все это дело автоматизировать

я бы пошел таким путем. лучше месяц потерять потом за пять минут долететь.

Denis Kolyako

не прочитано,
21 мар. 2010 г., 22:15:1221.03.2010
– ruf...@googlegroups.com
> на клиенте:
> SocketClient.instance.addEventListener(SocetClientCommands.MY_MEGA_COMMAND,
> onMyMegaCommand); // MY_MEGA_COMMAND - константа сгенерена скриптом


Вот честно, не понимаю, нафига гененировать тучу монструозных и бесполезных ивентов, когда можно обойтись строчкой:
(this._client[command.name] as Function).apply(this._client, command);

Денис Коляко
______________________________________________________________________
e...@timezero.ru | http://etcs.ru/ | http://timezero.com/


k0t0vich

не прочитано,
22 мар. 2010 г., 02:34:2122.03.2010
– ruFlash
>         Вот честно, не понимаю, нафига гененировать тучу монструозных и бесполезных ивентов, когда можно обойтись строчкой:
>         (this._client[command.name] as Function).apply(this._client, command);

тут сложность в разрастании класса this._client для которого нужно
создать, например 200 методов. в другом проекте будет невозможно
повторное использование кода - например там нужны только 2 из 200
команд..

я использую c protoBuf
message:Message = Message.getMessage(byteArray); // сериализуем в
нужный класс сообщения
message.execute(this.model,this.controller);
в клиенте имеется статичная ф-ция регистрации алиасов: - для каждого
проекта она может быть своя. в итоге мы можем спокойно использовать
готовые наборы команд в разных проектах.
кодогенерация может быть поставлена на поток с помощью .proto классов
- в реальности все классы достаточно удобно писались ручками. Не
доверяю я автоматам)

package flashfight.game.messages
{
import flash.net.registerClassAlias;
import flashfight.game.messages.input.*;
import flashfight.game.messages.output.*;
import flashfight.game.messages.inner.*;

/**
* Функция регистрации удалённых классов.
*/
public function registerMessagesAlias():void
{
//input Message
registerClassAlias("1", GameStatusMessage);
registerClassAlias("2", CreateGameMessage);
registerClassAlias("3", MoveCharacterMessage);
registerClassAlias("4", CharacterMessage);
registerClassAlias("5", ChangeCharacterMessage);
registerClassAlias("6", GetCharacterMessage);

//output Message

//inner Message
}

}

Flop Serg

не прочитано,
22 мар. 2010 г., 03:49:2922.03.2010
– ruf...@googlegroups.com
>
>        Вот честно, не понимаю, нафига гененировать тучу монструозных и бесполезных ивентов, когда можно обойтись строчкой:
>        (this._client[command.name] as Function).apply(this._client, command);


По мне выглядит удобнее когда именно ивенты..
да и многим программистам думаю тоже, взять к примеру доку по классу
Sprite там описаны методы, свойства и ивенты.
думаю врятли найдется человек который бы писал так:
(this.sprite["startDrag"] as Function).apply(this.sprite);

Да и вообще суть не в этом
я говорю что надо написать костяк, а рутиной вроде генерации
_монструозных _ но задокументированных и понятных каждому классов
пусть занимается скрипт.

Denis Kolyako

не прочитано,
22 мар. 2010 г., 04:39:3422.03.2010
– ruf...@googlegroups.com

> По мне выглядит удобнее когда именно ивенты..
> да и многим программистам думаю тоже, взять к примеру доку по классу
> Sprite там описаны методы, свойства и ивенты.

С тем успехом можно взять xml с описанными командами и прочитать его глазами.
А события и туча классов имеют сомнительный профит «автокомплита»
и явный фейл в плане скорости работы.

> думаю врятли найдется человек который бы писал так:
> (this.sprite["startDrag"] as Function).apply(this.sprite);

Вряд ли. Только причем тут вызов команды с произвольным именем и этот быдлокод?

Daniil Tutubalin

не прочитано,
22 мар. 2010 г., 04:39:3422.03.2010
– ruf...@googlegroups.com
>        Вот честно, не понимаю, нафига гененировать тучу монструозных и бесполезных ивентов, когда можно обойтись строчкой:
>        (this._client[command.name] as Function).apply(this._client, command);

1. Не будет работать Find Reference
2. Есть риск получить исключение.
3. Всё равно для команд придётся создавать тучу монструозных классов.

k0t0vich

не прочитано,
22 мар. 2010 г., 05:07:0122.03.2010
– ruFlash
Мне кажется что тема как-то не туда развилась..
1. >Основной вопрос куда убрать switch.

switch (event.code) {
case 1001:
break;
case 1002:
break;
case 1003
break;
...
case 1567:
break;
}

1-е предложение Дениса
>Тут хорошо подходит паттерн Команда.

2. каким образом регистрировать id команд
варианты:

>Использовать в протоколе имена (вместо id) команд и вызывать как методы.

>Константы выносятся в отдельный класс, от него наследуется этот класс.
Класс с константами называю обычно что-то вроде DataFormat и он
содержит все именования узлов XML, атрибутов и стандартных значений. И
от него наследуются и другие классы, активно использующие формат.

>Командному процессору конечно же нужна таблица "соответствий" между
ключами и выполняемыми командами, путей реализации этой таблицы есть
множество. Денис хранит ее в xml, вы делаете динамическую
регистрацию
в рантайме, кто-то пишет портянки свитч-кейсов.

мой вариант : на каждую комманду - отдельный класс - реализующий
интерфейcный метод execute на стороне клиента - для входящих комманд
- метод execute на стороне сервера для исходящих.
регистрация - в registerClassAlias. Способ подходит как для AMF так и
для protoBuf. методы являются мостами для связки моделей данных между
серверной и клиентской моделью.

Автоматизация создания классов комманд (сообщений): в protoBuf классы
генерятся для сервера и клиента на основе .ptroto файлов
Для AMF тоже существуют скрипты автогенерации: например java <-> AS3
для graniteDS.

300 - 400 классов комманд - не так уж и много для большого проекта.

Если использовать "голый" xml в качестве транспортного протокола
необходим метод сериализации входящего xml в класс команды - в этом
случае, скорей всего можно по-другому организовать - если не нравится
так:
xml -> parser по id -> создание кастомного класса команды -> вызов
execute.

kuril

не прочитано,
22 мар. 2010 г., 05:26:2722.03.2010
– ruFlash
> Вот честно, не понимаю, нафига гененировать тучу монструозных и бесполезных ивентов, когда можно обойтись строчкой:
> (this._client[command.name] as Function).apply(this._client, command);

Тут есть два недостатока:
1) идентификация по строке, числовой идентификатор предпочтительней
для серилизации и вообще сложной системы
2) идентификатор является именем функции, то есть комманда, некая
инструкция, требует от исполнителя того, что к ней не относится, как
например, чтобы копать, не обязательно иметь фамилию Копалкин.

kuril

не прочитано,
22 мар. 2010 г., 05:26:4522.03.2010
– ruFlash

> думаю врятли найдется человек который  бы писал так:
> (this.sprite["startDrag"] as Function).apply(this.sprite);

Это типичный пример из AS2, на нем вроде еще пишут.

Flop Serg

не прочитано,
22 мар. 2010 г., 05:31:5222.03.2010
– ruf...@googlegroups.com
>
>        С тем успехом можно взять xml с описанными командами и прочитать его глазами.
>        А события и туча классов имеют сомнительный профит <<автокомплита>>
>        и явный фейл в плане скорости работы.

блин я говорю НЕ ВАЖНО как реализовать выборку соответствия
обработчика к бинарным данным пришедшим с сокета
важно чтобы это делалось АВТОМАТИЧЕСКИ
1 одна оснавная дока
2 автосгенерированный клиентский и серверный код который на 100%
соответствует доке

а для оптимизации и скорости куча способов
например зачем передавать по бинарному сокету строку - имя команды
mySuperPuperCommand - 19байт
когда если комманд до 256 - передавать можно только один байт , если
комманд до 65536 - два байта
а названия и описания команд существуют только в доке и в
АВТОМАТИЧЕСКИ сгенерированном клиентском и серверном коде

еще про сложные типы данных - это тоже очень важно делать
автоматически - чтобы избежать ошибок

Denis Kolyako

не прочитано,
22 мар. 2010 г., 05:36:1622.03.2010
– ruf...@googlegroups.com

> а названия и описания команд существуют только в доке и в
> АВТОМАТИЧЕСКИ сгенерированном клиентском и серверном коде

Ну замечательно, только эта самая автоматика не нужна.

Flop Serg

не прочитано,
22 мар. 2010 г., 06:31:2722.03.2010
– ruf...@googlegroups.com
>
>        Ну замечательно, только эта самая автоматика не нужна.

возможно, в случаях с мелким количеством комманд в протоколе
и когда с протоколом работают только 2 человека, один со стороны
серверной части другой со стороны клиентской
всю эту автоматику написать можно хоть на АИРе если ничего другого под
рукой нету


плюсы есть
+сомнительный автокомплит и документация
+очень гибкий протокол, команды добавляются в 3 клика
+возможность работы неограниченно большой комманде с протоколом
+возможно реализовать любой архитектурный паттерн, придумать любой протокол
+если пойти ООП-шными более монстроподобными методами, все более гибкое,
этот протокол потом можно обвесить проксиками, повесить не только
на бинарный сокет, на хттп заросы на rtmp и тд..
минусы тоже
- только эта самая автоматика не нужна.

Илья намекал на дофига комманд.. решать ему..
фигли спорим я предложил вариант...

Илья Плотников

не прочитано,
22 мар. 2010 г., 07:22:5722.03.2010
– ruf...@googlegroups.com
22.03.2010 13:31, Flop Serg пишет:

> Илья намекал на дофига комманд.. решать ему..
> фигли спорим я предложил вариант...
>
Я решил делать через factory. Ну если интересно кому )
Ответить всем
Отправить сообщение автору
Переслать
0 новых сообщений