Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Реализация шаблона singleton в AS3 (http://www.flasher.ru/forum/showthread.php?t=113772)

Alexander Balaganov 08.07.2008 16:32

Реализация шаблона singleton в AS3
 
Посмотрите пожалуйста код. Вы используйте в своем коде шаблон "одиночка" или делаете это как-то подругому?

т.е. это объект, который (по-хорошему) может быть интсанциирован только один раз через метод getInstance()

Код:

public class RegistrationManagerProxy {

                static var registrationManager:RegistrationManagerProxy = null;

                var registrationManagerConnection:NetConnection=null;

                private function RegistrationManagerProxy() {
                        registrationManagerConnection = new NetConnection();
                        registrationManagerConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
                        registrationManagerConnection.connect("rtmp://localhost/registration_manager");
                }
                public static function getInstance():RegistrationManagerProxy {
                        if (registrationManager==null) {
                                registrationManager = new RegistrationManagerProxy();
                        }
                        return registrationManager;
                }
                public function registerAbonent(abonent:Object) {
                        trace("registerAbonent...");
                        trace("E-mail: "+ emailTextInput.text);
                }
                public function netStatusHandler(event:NetStatusEvent):void {
                        trace("connected is: " + netConnection.connected );
                        trace("event.info.level: " + event.info.level);
                        trace("event.info.code: " + event.info.code);

                        switch (event.info.code) {
                                case "NetConnection.Connect.Success" :
                                        trace("Congratulations! you're connected");
                                        break;
                                case "NetConnection.Connect.Rejected" :
                                        trace("Connection rejected by server :(");
                                        break;
                                case "NetConnection.Connect.Failed" :
                                        trace("Oops! you weren't able to connect");
                                        break;
                                case "NetStream.Play.Stop" :
                                        trace("The stream has finished playing");
                                        break;
                                case "NetStream.Play.StreamNotFound" :
                                        trace("The server could not find the stream you specified");
                                        break;
                                case "NetStream.Publish.BadName" :
                                        trace("The stream name is already used");
                                        break;
                                case "NetStream.Publish.Start" :
                                        trace("publish starting...");
                                        break;
                                case "NetStream.Play.Start" :
                                        trace("play starting...");
                                        break;
                        }
                }
        }


wvxvw 08.07.2008 16:41

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

BlooDHounD 08.07.2008 19:06

http://www.flasher.ru/forum/showthread.php?t=109596

ulik 08.07.2008 19:13

BloodHound >> Давно стало интересно, может ты сможешь объяснить. В чем необходимость использования подобной конструкции ("одиночка")??

Сколько не думал, так и не нашел применения...

BlooDHounD 08.07.2008 22:29

ulik, не понял :) это вопрос ко мне? или моя цитата?

ulik 08.07.2008 23:35

Это вопрос к тебе :)

BlooDHounD 09.07.2008 01:04

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

почему ExternalInterface должен быть всегда один?
потому что контэйнер у флэшки всегда один. пример ХТМЛ-страница.

пример использования синглтона такой:
1. допустим есть некий интерфейс IConnection, который объеденяет в некое множество классов. и ExternalInterface один из них.
Код:

public final class ExternalInterface implements IConnection {
...
}

2. предположим нам надо унаследоваться от EventDispatcher'a.
Код:

public final class ExternalInterface extends EventDispatcher {
...
}

но в данном случаи адоба выбрала вполне логичный путь:
1. колбэк тоже должен быть единсвенный, поэтому наследование от диспатчера не нужно.
2. имплементация интерфейса тоже не нужна. так как его не с чем объеденять.

ShareObject - вполне заслуженный мульти тон.


резюме по поводу данного топика: тут синглтон даже близко не нужен.

ulik 09.07.2008 13:02

Угу. Спасибо. Просто я уже не раз видел "обсуждение сингла" и даже пробовал сделать "тест" проект, чтобы его задействовать, но так и не смог найти именно тот случай, где нужен "класс-сингл". Да и на моем опыте не было пока необходимости в подобном. Другое дело - мультитон. Тут проще :)

wvxvw 09.07.2008 13:35

Вот, на мой взгляд, уместная реализация синглтона. Почему именно синглтон? - потому, что он общается не напрямую с получателем/отправителем информации, а через менеджер. У менеджера есть еще около 7-8 вариантов получения внешней информации, которая должна выглядеть одинаково для получателей. Т.е. в данном случае я посылаю картинки, но, я могу те же картинки получить не из сокета, а просто загрузив с диска или распарсив информацию получаемую из другого URLStream'a и т.п. получателей информации может быть неопределенное количество, сокет может сменить порт и ИП в процессе работы, но получателя информации это не должно волновать - поэтому менеджер создает его 1 раз и на всегда.
(упрощенный вариант)
Код:

/**
* ...
* @author wvxvw
*/
package phpconnect
{
        import flash.events.Event;
        import flash.events.IOErrorEvent;
        import flash.events.ProgressEvent;
        import flash.events.SecurityErrorEvent;
        import flash.net.Socket;
        import flash.utils.ByteArray;
        import flash.utils.Endian;
        import utilities.LoaderEvent;
        import LoadManager;
        import App;
        import player_internal;
       
        import debug;
       
        public class SocketReceiver extends Socket
        {
                public static const US_ASCII:String = 'us-ascii';
               
                private var srPort:int;
                private var srIP:String;
                private var _resourceName:String;
                private var _lm:LoadManager;
               
                public function SocketReceiver(i:Initializer)
                {
                        super();
                        if (i == null) throw new Error("SocketReciever: Argument must not be null!");
                        endian = Endian.LITTLE_ENDIAN;
                        addEventListener(Event.CONNECT, dispatchOnConnected);
                        addEventListener(ProgressEvent.SOCKET_DATA, dispatchOnData);
                        addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
                        addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
                       
                }
                public function xconnect():void
                {
                        if (!_lm) _lm = App.getInstance().player_internal::_loader;
                        srPort = _lm.player_internal::getAvailableSocketPort();
                        srIP = _lm.player_internal::getAvailableSocketIP();
                        super.connect(srIP, srPort);
                }
                public function setResourceName(s:String):void
                {
                        _resourceName = s;
                }
                private function dispatchOnConnected(evt:Event):void
                {
                        dispatchEvent(new LoaderEvent(LoaderEvent.LOADER_CONNECTED, null, LoadManager.DRAW));
                }
                private function dispatchOnData(evt:ProgressEvent):void
                {
                        if (bytesAvailable == 0) return;
                        var ba:ByteArray = new ByteArray();
                        ba.endian = Endian.LITTLE_ENDIAN;
                        readBytes(ba);
                        ba.position = 0;
                        dispatchEvent(new LoaderEvent(LoaderEvent.LOADER_LOADED, ba, LoadManager.DRAW));
                }
                private function handleIOError(evt:IOErrorEvent):void
                {
                        debug(this, 'IO Error', evt);
                }
                private function handleSecurityError(evt:SecurityErrorEvent):void
                {
                        debug(this, 'SECURITY Error', evt);
                }
                public function sendRequest(s:String):void
                {
                        super.writeMultiByte(s, US_ASCII);
                        super.flush();
                }
                public function sendBytes(b:ByteArray):void
                {
                        super.writeBytes(b);
                        super.flush();
                }
               
                private static var _instance:SocketReceiver;
               
                public static function getInstance():SocketReceiver
                {
                        if (!_instance) {
                                _instance = new SocketReceiver(new Initializer());
                        }
                        return _instance;
                }
        }
       
}
internal final class Initializer
{
        public function Initializer(){}
}


etc 09.07.2008 13:43

wvxvw, а что с английским? :)

wvxvw 09.07.2008 14:11

А что? фз... я еще не дожил спелчекером код проверять =) Вроде обычно более менее грамотно пишу... ну так на первый взгляд никаких ошибок не обнаружил? Что не так? =)

etc 09.07.2008 14:13

Цитата:

Сообщение от wvxvw (Сообщение 751052)
Что не так? =)

Название класса.

wvxvw 09.07.2008 14:19

Ааааа.... млин, ну, опечатался, ща поправлю =)

BlooDHounD 09.07.2008 14:49

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

wvxvw 09.07.2008 15:58

не, не скажет, моя флешка - часть большого проекта, в котором это взаимодействие описано. По техническим причинам она не сможет коннектится к >2 сокетам. Так устроена остальная часть, которая от меня не зависит. Это одно из соображений оптимизации, и оно очень важно в моем случае. Т.е. флешка запускается локально на сервере (не пользователем, а таском), но тредов запустивших флешку может быть много (т.е. будет запущено много копий флешки). Так что запустить +1 сокет для каждой копии (каждого нового треда) - на это никто не пойдет...

BlooDHounD 09.07.2008 16:29

wvxvw, скажи, а методы connect и close могут вызывать кто угодно и где угодно?
синглтон в твоём случаи должен ограничить разработчика от нежелательный действий с соединением. твоё соединение теоретически одно. заметь только теоретически, но закрыть его или открыть может кто угодно и откуда угодно. нафига тут синглтон? :)

wvxvw 09.07.2008 16:32

Это не полное содержание класса =) он на самом деле на много больше, connect и close никто не может вызвать, просто в примере это не описано.

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

BlooDHounD 09.07.2008 17:30

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

wvxvw 09.07.2008 17:45

Вероятность необходимости еще одного такого же класса, с таким же функционалом практически равна 0... т.е. предположим, что в ближайший год производительность серверов в среднем увеличится в 2 раза (уже как-то стремно =), ну ок, и мы решим задействовать открывшийся ресурс для того, чтобы прицепить туда еще 1 сокет (ну, не так уж много это и занимает, в конце концов =), но, что он будет делать? Сокет-отправитель уже есть, сокет-получатель - тоже. Приложение должно выполнять 2 операции: получать и посылать. Чего бы еще ему такое придумать? - Я вижу вариант только такой: в рандомально выбраное время откывать дверцу сидирома и загробным голосом произносить "my preeeeecioussssss.....". :D

BlooDHounD 09.07.2008 20:24

Олег :) у нас в проектах обычно минимум 2 сокета. хотя можно конечно загрузить всё в один. а ещё можно использовать один сервер, где надёжнее использовать 4.

ты мне вот скажи зачем там нужен синглтон?
Цитата:

Просто, представь, что с другой стороны могло бы быть: имеется пару десятков отправителей, которые должны посылать в один единственный сокет - а у каждого из них своя копия этого сокета, и кому это нужно придумывать как сделать синхронизацию и предотвращение конкурентных обращений? А так, каждый, кто хочет чего-то послать ломится по одному и тому же адресу. Если посылать не успеваем - строим очередь, ну и т.д.
да хоть 100 отправителей :) если говорить на уровне проектирования, то сокетов быть не должно в принципе. все твои "отправители" должны работать с неким абстрактным интерфейсом имеющим пару методов. как пример метод call у NetConnection. что происходит внутри должно быть чёрным ящиком. и откуда берётся тоже. писать во всех твоих отправителях RegistrationManagerProxy.getInstance(); меня бы задолбало, а сохронять ссылку нельзя, так как ты не можешь знать, что происходит в getInstance(). вдруг там нечто важное происходит? в общем это не тру-путь.

wvxvw 09.07.2008 21:11

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

[а] "пришли картинку" ---|      [источник картинок]
[б] "пришли звук" --- [менеджер] ---| ----[источник звуков]
[в] "пришли шрифт" ------|    |  [источник шрифтов]
                              |
                              |
                <сокет == источник управляющей информации + ресурсов>

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

BlooDHounD 09.07.2008 23:52

это почему на флэшке должен быть один сокет?

wvxvw 10.07.2008 01:07

аааааа =))) нет, должно быть ровно 2, но разных, а больше быть не должно =) Т.е. как можно догадаться из названия класса есть еще и SocketSender =)
Короче, писал-писал, и все стер =) Вобщем, представь, тебе налили 10 тарелок супа, но нафик тебе для этого 10 ложек? Конечно, можно извратится и позапихивать их как-нть между пальцами, но есть от этого верняк удобнее не станет =)

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

BlooDHounD 10.07.2008 01:14

wvxvw, зачем статические методы? твой синглтон это те же "статические методы" только вид сбоку и запись длинее.

а почему 2? а я говорю 3!!! нет! даже 4! откуда такие ограничения? зачем вообще их считать? зачем думать сколько их? зачем вообще думать что они из себя предстваляют?

P.S.: Денис, спаси меня :) я не могу объяснить, а ты могуч :)

wvxvw 10.07.2008 01:24

Потому что больше не надо - ну нечего мне с ними делать =) ну, попробуй сесть сразу на 4 стула =)
ЗЫ. запись вида SocketSender.getInstance() встречается в коде ровно 1 раз... при чем была технично вставлена автокомплитом! %) я ее даже не писал =)

etc 10.07.2008 01:27

Цитата:

Сообщение от BlooDHounD (Сообщение 751223)
P.S.: Денис, спаси меня :) я не могу объяснить, а ты могуч :)

Хе-хе :quiet:

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

wvxvw 10.07.2008 01:32

Да потому что в моей конкретной задаже есть Н тарелок с супом, а скушать их должен 1 человек =) Если вы ему для этого дадите Н ложек, где Н много блольше 1, он просто растеряется, и станет их использовать не по назначению =)

etc 10.07.2008 01:37

Цитата:

Сообщение от wvxvw (Сообщение 751235)
Да потому что в моей конкретной задаже есть Н тарелок с супом, а скушать их должен 1 человек =) Если вы ему для этого дадите Н ложек, где Н много блольше 1, он просто растеряется, и станет их использовать не по назначению =)

Причем тут ложки? Потребителем является не человек, а контроллеры. Очень быстро может возникнуть ситуация, когда контроллеров будет больше одной штуки и они будут отвечать за совершенно разные соединения.

wvxvw 10.07.2008 01:45

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

BlooDHounD 10.07.2008 03:02

wvxvw, кароче. у тебя синглтон ради сингтона, а не от необходимости.

ты приплёл синглтон, когда тебе всего лиш "нужен один" конекшен, а не он "должен быть один". надеюсь я доходчиво объяснил?

Alexander Balaganov 11.07.2008 00:34

Вставлю свои 5 копеек, уворачиваясь от вил.

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

Ее я и решил применить в флэш-проекте на AS3.

1) Я отнаследовал Main.as от MovieClip
2) Я создал класс RegistrationForm и повесил listener на кнопку registrationButton
3) В callback методе я использовал RegistrationManager.getInstance().register()

Но это действительно не лучший пример, так как класс
RegistrationManager используется один раз при регистрации юзера.

Заменим его на класс ApplicationManager который будет общаться с сервером вызывая методы NetConnection.call и получая callbacks.
Фактически, этот класс будет оберткой NetConnection и он должен быть единственным, т.к. нужен только один коннект к медиа серверу.

В чем здесь неоправданное использование шалона "одиночка"?
Как этот функционал реализовать по другому?

wvxvw 11.07.2008 01:12

BlooDHounD:
Если он мне нужен всего один, то я и буду делать всего один, зачем мне 2 одинаковых? В этом и задача синглтона - намерено не дать создать другой такой же объект... Конекшн уникальный по своим параметрам, и другого такого как он быть не может, потому что второму такому делать нечего будет. А нужен будет еще один, но с другим функционалом - так я и создам другой...

Alexander Balaganov:
Собственно, наш спор до этого был о том, что лучше использовать:
Manager.someStaticMethod(); или Manager.getInstance().someMethod();
Мое мнение такое, что если удобнее пользоваться методами объекта, у которого нужно вызвать метод (например, нам его нужно унаследовать от EventDispatcher, чтобы можно было рассылать события из него, а не через какoе-нибудь опять же статическое свойство хранящее ссылку на экземпляр EventDispatcher, то, мое мнение, что удобнее использовать синглтон. Но если класс ни от чего наследовать не нужно, то проще обойтись статическими методами.

BlooDHounD 11.07.2008 03:32

пример:
1. есть хитрый класс обрабатывающий соединение. ему для работы нужен сериализатор;
2. есть сериализатор. ему для работы нужны параметры сереализации;
3. нужно сделать игровой поток, и чатовый поток. оба потока в принципе работают можно объединить в один, но мы хотим их развивать независимо;
4. мы берём хитрое соединение, сереализатор, меняем параметры, получаем соединение;

каждого соединение существует по одному. логично! зачем нам 2 чатовых соединения? из вашей логики мы должны сделать следущие:
1. сделать синглтон-класс ChatConnection;
2. сделать сингтонр-класс GameConnection;

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

Цитата:

Сообщение от wvxvw (Сообщение 751398)
Если он мне нужен всего один, то я и буду делать всего один, зачем мне 2 одинаковых?

сделай один, зачем делать из него синглтон?
Цитата:

Сообщение от wvxvw (Сообщение 751398)
В этом и задача синглтона - намерено не дать создать другой такой же объект...

Ошибка! задача синглтона совсем в другом. если ты "намерянно" пытаешь создать второй такой объект, значит тебе надо! и ты ошибся проектируя класс. Задача синглтона разграничить твои права. Дело в том, что работая с синглтоном, ты не знаешь создаётся ли объект, или этот один и тот же экземпляр. логика шаблона заключается в том, что информация, об источнике появления объекта тебе не известна. но ты точно знаешь, что это ЭКЗЕМПЛЯР.
Цитата:

Сообщение от wvxvw (Сообщение 751398)
Конекшн уникальный по своим параметрам, и другого такого как он быть не может, потому что второму такому делать нечего будет.

тоесть есть внешние параметры его создания? тогда какой же это синглтон?
Цитата:

Сообщение от wvxvw (Сообщение 751398)
А нужен будет еще один, но с другим функционалом - так я и создам другой...

функционал может быть тот же самый, но "target" другой.
Цитата:

Сообщение от wvxvw (Сообщение 751398)
Собственно, наш спор до этого был о том, что лучше использовать:
Manager.someStaticMethod(); или Manager.getInstance().someMethod();

неа! это значит ты меня не внимательно читал. тут вообще никаких статиков, и никаких гетинстанс быть не должно.
Цитата:

Сообщение от wvxvw (Сообщение 751398)
Мое мнение такое, что если удобнее пользоваться методами объекта, у которого нужно вызвать метод (например, нам его нужно унаследовать от EventDispatcher, чтобы можно было рассылать события из него, а не через какoе-нибудь опять же статическое свойство хранящее ссылку на экземпляр EventDispatcher, то, мое мнение, что удобнее использовать синглтон. Но если класс ни от чего наследовать не нужно, то проще обойтись статическими методами.

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

wvxvw 11.07.2008 04:40

Млин, короче... я не хочу спорить... ты все не правильно понял, ну да ладно, не важно.

ulik 11.07.2008 13:52

Цитата:

P.S.: Денис, спаси меня я не могу объяснить, а ты могуч
- порадовало :))

Какие страсти-то разыгрались - брррр... :)
А инициатором был я :quiet:
В ЭтогЕ, я так, честно говоря, и не нашел именно "синглтон случая", когда без него, ну, никуда. Облазил весь инет - пока что-то жесткой необходимости в классе-сингле не нашел. Возможно, сам пойму, когда столкнусь с такой задачей, где именно он нужен будет...


Часовой пояс GMT +4, время: 00:22.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.