![]() |
|
||||||||||
|
|||||||
|
|
« Предыдущая тема | Следующая тема » |
| Опции темы | Опции просмотра |
|
![]() |
![]() |
|
|||||
|
[+1 16.03.13]
[+1 22.03.13] Регистрация: Dec 2012
Сообщений: 100
|
У меня конечно изначально была идея взять за основу шаблон состояние:
package server_exchange.socket_4444 { // Состояние: Контекстный класс internal class WorksSocket { /*////////////////////////////////////// Список состояний //////////////////////////////////////*/ // Соединение с сокетом не установлено (ожидаются действия) private var state1No:IState; // Соединение с сокетом устанавливается private var state2SocketConnecting:IState; // Соединение с сокетом успешно установлено (ожидаются действия) private var state3SocketEstablished:IState; // Выполняется отправка данных на сервер private var state4SocketReading:IState; // Выполняется приём данных с сервера private var state5SocketWriting:IState; // Текущее состояние private var iStateSocket:IState; public function WorksSocket() { // Инициализируем все конкретные состояния this.state1No = new State1No(this); this.state2SocketConnecting = new State2SocketConnecting(this); this.state3SocketEstablished = new State3SocketEstablished(this); this.state4SocketReading = new State4SocketReading(this); this.state5SocketWriting = new State5SocketWriting(this); // Устанавливаем первоначальный статус при инициализации конструктора this.iStateSocket = this.state1No; } /*///////////////////////////////////////////////////// Выполнение события в установленном состоянии /////////////////////////////////////////////////////*/ public function set ExecuteState(comand:String):void { switch(comand) { case ComandName._command1SocketConnect: this.iStateSocket.Command1SocketConnect(); break; case ComandName._command2SocketConnecting: this.iStateSocket.Command2SocketConnecting(); break; case ComandName._command3SocketWrite: this.iStateSocket.Command3SocketWrite(); break; case ComandName._command4SocketRead: this.iStateSocket.Command4SocketRead(); break; } } /*///////////////////////////////////////////////////// Устанавливает состояние /////////////////////////////////////////////////////*/ public function set SetState(state:String):void { switch(state) { case StateName._state1No: this.iStateSocket = this.state1No; break; case StateName._state2SocketConnecting: this.iStateSocket = this.state2SocketConnecting; break; case StateName._state3SocketEstablished: this.iStateSocket = this.state3SocketEstablished; break; case StateName._state4SocketReading: this.iStateSocket = this.state4SocketReading; break; case StateName._state5SocketWriting: this.iStateSocket = this.state5SocketWriting; break; } } } // Конец класса } // Конец пакета package server_exchange.socket_4444 { // Состояние: команды для выполнения действий в каком-либо из состояний internal interface IState { // Выполняет команду на соединение с сокетом function Command1SocketConnect():void; // Связь с сокетом устанавливается function Command2SocketConnecting():void; // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) function Command3SocketWrite():void; // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) function Command4SocketRead():void } } Действия состояний: package server_exchange.socket_4444 { // Действия, выполняемые в каком-либо состоянии internal class ComandName { // Выполняет команду на соединение с сокетом internal static const _command1SocketConnect:String = 'Command1SocketConnect'; // Связь с сокетом устанавливается internal static const _command2SocketConnecting:String = 'Command2SocketConnecting'; // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) internal static const _command3SocketWrite:String = 'Command3SocketWrite'; // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) internal static const _command4SocketRead:String = 'Command4SocketRead'; } } Непосредственно сами состояния: package server_exchange.socket_4444 { // Конкретное состояние internal class StateName { // Соединение с сокетом не установлено (ожидаются действия) internal static const _state1No:String = 'State1No'; // Соединение с сокетом устанавливается internal static const _state2SocketConnecting:String = 'State2SocketConnecting'; // Соединение с сокетом успешно установлено (ожидаются действия) internal static const _state3SocketEstablished:String = 'State3SocketEstablished'; // Выполняется отправка данных на сервер internal static const _state4SocketReading:String = 'State4SocketReading'; // Выполняется приём данных с сервера internal static const _state5SocketWriting:String = 'State5SocketWriting'; } } Описание состояний: package server_exchange.socket_4444 { internal class State1No implements IState { // Состояние private var worksSocket:WorksSocket; public function State1No(worksSocket:WorksSocket) { this.worksSocket = worksSocket; } // Выполняет команду на соединение с сокетом public function Command1SocketConnect():void { } // Связь с сокетом устанавливается public function Command2SocketConnecting():void { } // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) public function Command3SocketWrite():void { } // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) public function Command4SocketRead():void { } } } package server_exchange.socket_4444 { internal class State2SocketConnecting implements IState { // Состояние private var worksSocket:WorksSocket; public function State2SocketConnecting(worksSocket:WorksSocket) { this.worksSocket = worksSocket; } // Выполняет команду на соединение с сокетом public function Command1SocketConnect():void { } // Связь с сокетом устанавливается public function Command2SocketConnecting():void { } // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) public function Command3SocketWrite():void { } // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) public function Command4SocketRead():void { } } } package server_exchange.socket_4444 { internal class State3SocketEstablished implements IState { // Состояние private var worksSocket:WorksSocket; public function State3SocketEstablished(worksSocket:WorksSocket) { this.worksSocket = worksSocket; } // Выполняет команду на соединение с сокетом public function Command1SocketConnect():void { } // Связь с сокетом устанавливается public function Command2SocketConnecting():void { } // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) public function Command3SocketWrite():void { } // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) public function Command4SocketRead():void { } } } package server_exchange.socket_4444 { internal class State4SocketReading implements IState { // Состояние private var worksSocket:WorksSocket; public function State4SocketReading(worksSocket:WorksSocket) { this.worksSocket = worksSocket; } // Выполняет команду на соединение с сокетом public function Command1SocketConnect():void { } // Связь с сокетом устанавливается public function Command2SocketConnecting():void { } // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) public function Command3SocketWrite():void { } // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) public function Command4SocketRead():void { } } } package server_exchange.socket_4444 { internal class State5SocketWriting implements IState { // Состояние private var worksSocket:WorksSocket; public function State5SocketWriting(worksSocket:WorksSocket) { this.worksSocket = worksSocket; } // Выполняет команду на соединение с сокетом public function Command1SocketConnect():void { } // Связь с сокетом устанавливается public function Command2SocketConnecting():void { } // Связь с сокетом установлена, выполняется отсылка данных на сервер (запись в сокет) public function Command3SocketWrite():void { } // Связь с сокетом установлена, выполняется приём данных с сервера (чтение из сокета) public function Command4SocketRead():void { } } } package server_exchange.socket_4444 { import flash.net.XMLSocket; import flash.events.IEventDispatcher; import flash.events.Event; import flash.events.DataEvent; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.events.SecurityErrorEvent; // Структута соединения на сокет internal class StructSocket { private var socket:XMLSocket; public function StructSocket() { this.socket = new XMLSocket(); configureListeners(socket); if (hostName && port) { socket.connect(hostName, port); } } public function send(data:Object):void { socket.send(data); } private function configureListeners(dispatcher:IEventDispatcher):void { dispatcher.addEventListener(Event.CLOSE, closeHandler); dispatcher.addEventListener(Event.CONNECT, connectHandler); dispatcher.addEventListener(DataEvent.DATA, dataHandler); dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler); dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); } private function closeHandler(event:Event):void { trace("closeHandler: " + event); } private function connectHandler(event:Event):void { trace("connectHandler: " + event); } private function dataHandler(event:DataEvent):void { trace("dataHandler: " + event); } private function ioErrorHandler(event:IOErrorEvent):void { trace("ioErrorHandler: " + event); } private function progressHandler(event:ProgressEvent):void { trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal); } private function securityErrorHandler(event:SecurityErrorEvent):void { trace("securityErrorHandler: " + event); } } // Конец класса } // Конец пакета |
|
|||||
|
буду краток
модератор форума
Регистрация: Sep 2003
Адрес: Ближайшее Замкадье
Сообщений: 3,110
Записей в блоге: 28
|
1. Реализовывать наблюдатель не нужно - используйте нативные для AS3 события.
2. Я бы сделал MVC, (не то, что оно тут уместно, но у меня всегда MVC получается ![]()
__________________
Отряд Котовскага |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
Цитата:
Дальше по коду. Зачем он здесь? Почему данный класс не предоставляет static accessors к своей функциональности? Он же ничем нужным и полезным не параметризуется. А если параметризуется, автоматически могут существовать несколько экземпляров, параметризованных по-разному. И вообще, посмотрите, зачем же используется singleton. Даже там, где его можно было бы использовать, его стоит на нормальный dependency injection переделать. Не было никогда наблюдателя в сетевых обменах. Не было, нет и не будет. Потому что там нет "состояния" в явном виде. Там есть события. Поэтому максимум, что можно сделать - это message bus. Хотя вру, я делал что-то подобное на observer, но это было для состояния одного конкретного обмена, а не для всего уровня коммуникаций. Кстати, а что должен делать клиент, чтобы какой-нибудьб справочник с сервера подгрузить? И зачем там вообще интерфейс, если реализация примерно одна предполагается и не инжектится снаружи? Гениально! Если мне захочется использовать этот обмен в другом проекте, мне нужно будет его исходный код менять? Ведь объекты в другом приложении другие, а я люблю их иметь строго типизированными... А смысл? Я вам туда null передам и будет у меня второй SocketExchange. И ничего даже не сломается. И даже в некоторых случаях это будет единственным нормальным вариантом использования этой библиотеки (две подписки на "одно и то же" сообщение). Почему не notifyObserver то??? // Реализует процесс подписки public function AddObserver(obserName:String,obserFunc:Function):void { this.observers[obserName] = obserFunc; } for(var notify:String in this.observers) { // Применяется метод update из интерфейса Observer if (notify == this.observerName) Обмен данными через поля класса вместо параметров. Типичный антипаттерн. Выпиливается при первом же рефакторинге (и заменяется на параметры). Потому что отлаживать такое счастье тяжелее, чем передачу параметров. И тестировать отдельные функции проще, чем объекты с состоянияем. Цитата:
Вы себя на место пользователя вашей библиотеки ставили? Почему функция "начать загрузку" у вас называется "установить входные данные" и в качестве входных данных устанавливается функция? Ведь не понятно же... А это должно быть здесь? При переносе этой библиотеки в другой проект мне придется еще и сообщения править. Я всегда думал, что конкретное форматирование ошибок - дело UI-части, а не самого транспорта. А на уровне транспорта нужно предоставить способ различать разные коды ошибок. Зачем там декоратор? Что он будет добавлять? О как. А если там не XML? Банальный текст, например (ну не работает сервер)? У вас ведь все умрет (в обычном плеере). Ошибка куда-нибудь улетит и больше не вернется. Почему класс не параметризуется внешним парсером - не понятно. Хотя нет, наверное, понятно. Это из-за излишней любви к синглтонам и неумения собирать граф служб во время запуска программы. У этого класса уже есть ответственность (обработка ошибок транспортного уровня), поэтому парсинг можно бы куда-нибудь в другое место вынести. Можно еще этому классу добавить обработку логических ошибок (внутренняя ошибка сервера, данные не распарсились). Ваши парсеры не валидируют структуру. Т.е. у вас все поля опциональные (и весь остальной код к этому должен быть готов). P.S. Я не помню, что у вас в той библиотеке происходит при двух параллельных запросах (т.е. второй приходит тогда, когда первый еще не отработал). Вроде бы что-то нехорошее... |
|
|||||
|
[+1 16.03.13]
[+1 22.03.13] Регистрация: Dec 2012
Сообщений: 100
|
Цитата:
Модель - занимается состоянием, значит строим на основе: Наблюдатель или Состояние Представление - отображает состояние, значит строим на основе: Компановщик или Декоратор Контроллер - обрабатывает действия, значит строим на основе: Наблюдатель или Состояние конечно я могу и ошибаться В этом я с вами полностью солидарен. Вот только где взять удобный API? |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
Цитата:
. Вот в этом в основном и состоит работа программиста (разработчика ПО). Нужно:1. Понять, чем вообще занимается приложение 2. Понять, как будет устроен обмен с сервером. Подсказка - обмен может быть устроен неоднородно, может быть несколько различных типов обмена. 3. Детализировать и формализовать (не обязательно слишком усердствовать) требования к API (как то обработка ошибок, мониторинг процесса загрузки). 4. Определиться, что может измениться в ближайшее время (неделя, месяц, три месяца, год). 5. Определиться, что вряд ли будет меняться в ближайшее время (месяц, три месяца, год). 6. Сделать draft API. Типичные действия пользователей библиотеки (на момент разработки) должны делаться легко и просто. 90% действий в идеале должны выполняться за 1-3 строчки кода. 1 строчка - идеал, но не всегда достижима. Все зависит от того, насколько "общей" получается задача. 7. Реализовать этот внешний API. О внутренностях можно не очень заботится. При необходимости их можно будет переписать или отрефакторить. Главное, что внешний API будет удобен. 8. На практике отследить, что API действительно удобен. При необходимости исправить API. Вот примерно в том, что выше, и состоит работа разработчика ПО. А код - это побочный продукт разработки. Через несколько подобных реализаций вы начнете видеть похожие элементы в них и получите несколько удобных кубиков, из которых можно что-то собрать. Только вот "кубики" слишком универсальными все равно не поулчатся. У разных приложений разные требования, разные сценарии работы и т.п. Поэтому процентов 50 вы, может, на кубиках наберете (упрощение API URLLoader'а, какие-то совсем базовые вещи). А остальные 50% все равно придется реализовывать под конкретный проект. То же сетевое API во флеш достаточно высокоуровневое, до API уровня приложения там не так много можно добавить. |
|
|||||
|
[+1 22.04.13]
[+1 06.03.13] Регистрация: Apr 2012
Сообщений: 239
|
вы тратите слишком много времени на ерунду (комментирование закрывающих скобок это вообще фаталити)... пишите легкий для восприятия код KISS - keep it simple stupid. Правильно декомпозируйте код, давайте методам понятные имена и всё у вас получится.
|
|
|||||
|
[+1 16.03.13]
[+1 22.03.13] Регистрация: Dec 2012
Сообщений: 100
|
Цитата:
2. Разумеется обмен с сервеном будет неоднородным, единственное что объединяет обмен данных между собой, это то что они в XML формате 3. Обработка ошибок, мониторинг процесса передачи данных, парсинг данных после их принятия с сервера из XML в объект, формирование данных для отправки их на сервер из объекта в XML 4. Возможно, добавятся новые XML объекты (как приёма, так и передачи данных) 5. Возможно, изменятся существующие XML объекты 6. Вот с этим сложнее, почему-то на практике получается, что я хоть и отправляю одни какие-либо данные ввиде объекта-параметра методу нужного класса. В итоге получается что в этот объект приходится запихивать кучу функций, таким образом не получается уложиться в 1-3 строчки. 7. Если это построение структуры, то беру за основу либо шаблон Компановщик, либо Декоратор; если это какое-либо поведение, то беру за основу либо шаблон Наблюдатель, либо Состояние 8. В том плане что шаблон проектировани удобен - тут сомнений нет, другое дело, что он не всегда в тему может оказаться. За совет, спасибо Добавлено через 6 минут Цитата:
Добавлено через 9 минут Комментирую окончание метода или класса, чтобы удобнее было определять где начинается и где заканчивается, если в случае надобности добавлять что-то ещё. |
|
|||||
|
буду краток
модератор форума
Регистрация: Sep 2003
Адрес: Ближайшее Замкадье
Сообщений: 3,110
Записей в блоге: 28
|
И самое главное!
Начните с использования конвенций наименований, принятых в AS3.
__________________
Отряд Котовскага |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
Ну время сборки это не принципиально. Оно еще и зависит от используемых инструментов. Да и штатный компилятор параллелить не умеет вроде бы сборку, так что задействовано одно ядро.
Неоднородности - это не только форматы данных. Это еще и схемы обмена. Либо запрос-ответ, либо асинхронный обмен сообщениями. Это тоже влияет на API (очевидно, что API для синхронного и асинхронного обмена совершенно различны). Обработка ошибок вполне может быть на разных уровнях. Очевидно, что на "транспортном" уровне корректно ее обработать (вывести сообщение, например) сложно. Это автоматически дает несколько различных уровней со своим API. Соответственно, различные клиенты используют разные уровни API (обычно - только высокоуровневые). Цитата:
Цитата:
Цитата:
|
![]() |
![]() |
Часовой пояс GMT +4, время: 14:34. |
|
|
« Предыдущая тема | Следующая тема » |
|
|