User's collector

Внимание!   Данная опция будет доступна только после того, как вы авторизуетесь.
   запомнить меня 
24 октября 2007

Варианты реализации паттерна Singleton в языке ActionScript 3

Основное назначение паттерна Singleton — обеспечение гарантии того, что в программе будет существовать только один экземпляр класса и предоставление глобальной точки доступа к этому экземпляру. Достигается это путем запрета прямого инстанцирования экземпляров класса при помощи конструктора и описания в классе специального статического метода, отвечающего за создание и доступ к единственному экземпляру класса. В ActionScript 3 нельзя обозначить конструктор класса как private и в связи с этим становится невозможным использование «классической» реализации паттерна без дополнительной доработки ее напильником. В этой статье я хотел собрать воедино весь известный мне материал, касающийся реализации паттерна Singleton в языке ActionScript 3.

Кому лень просматривать листинги возможных вариантов реализации паттерна, и кто хочет побыстрее узнать к какому выводу я пришел, при выборе предпочтительного варианта, тот может смело крутить ползунок браузера вниз — к части «Подводим итоги» — и испортить себе тем самым всю интригу.

Я еще раз хочу напомнить вам, что Одиночка должен обеспечивать две вещи:

  1. Запретить создание экземпляров класса при помощи конструктора.
  2. Предоставлять доступ к единственному экземпляру класса при помощи статического метода или свойства, защищенного от записи.

Давайте, не забывая об этих обязанностях, рассмотрим, как они выполняются в той или иной реализации паттерна.

Вариант с созданием экземпляра на этапе инициализации статических членов класса

Это самый простой вариант, не требующий никаких дополнительных действий. Как известно, все классы приложения экспортируются в определенный кадр ролика (по умолчанию в первый) и именно в этом кадре и происходит их инициализация. Как я себе представляю последовательность: сначала создается шаблон каждого класса, затем инициализируются статические члены классов и, лишь после этого, начинают создаваться конкретные экземпляры объектов классов. Т. е. если мы создадим единственный экземпляр Singleton на этапе инициализации статических членов класса, то мы совершенно точно создадим его до того, как это будет сделано где-нибудь еще в программе. Добавим в конструктор проверку на существование единственного экземпляра и Одиночка готов:

Actionscript:
  1. package
  2. {
  3.     /**
  4.      * Пример реализации паттерна Одиночка на языке ActionScript 3.
  5.      * с созданием экземпляра на этапе инициализации класса.
  6.      *
  7.      * @author  Yuri "Barmaley" Yarovoy
  8.      * @version 1.0
  9.      */
  10.     public class Singleton
  11.     {
  12.  
  13.         // Ссылка на единственный экземпляр класса.
  14.         private static var __instance:Singleton = new Singleton();
  15.  
  16.         /**
  17.          * Возвращает ссылку на единственный экземпляр класса.
  18.          */
  19.         public static function get instance():Singleton
  20.         {
  21.             return __instance;
  22.         }
  23.  
  24.         /**
  25.          * Конструктор.
  26.          */
  27.         public function Singleton()
  28.         {
  29.             if(__instance)throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте Singleton.instance.");
  30.         }
  31.     }
  32. }

Однако этот вариант плох тем, что не поддерживает «ленивую» инициализацию. Т. е. единственный экземпляр Одиночки создается совместно с инициализацией статических членов класса и с тех пор присутствует в программе, даже если он там еще и не нужен. Кроме того, если Одиночка имеет большое количество свойств, то это может заметно сказаться на времени запуска приложения. Вариант поддерживает использование явного геттера get instance для обеспечения доступа к единственному экземпляру класса.

Вариант с проверкой булева флага на возможность инстанцирования

Actionscript:
  1. package
  2. {
  3.     /**
  4.      * Пример реализации паттерна Одиночка на языке ActionScript 3
  5.      * с проверкой специального флага на возможность инстанцирования.
  6.      *
  7.      * @author  Yuri "Barmaley" Yarovoy
  8.      * @version 1.0
  9.      */
  10.     public class Singleton
  11.     {
  12.         private static var __instance:Singleton;
  13.         private static var __allowInstantiation:Boolean = false;
  14.  
  15.         public static function get instance():Singleton
  16.         {
  17.             if(!__instance)
  18.             {
  19.                 // Разрешаем создание экземпляра класса.
  20.                 __allowInstantiation = true;
  21.                 // Создаем экземпляр.
  22.                 __instance = new Singleton();
  23.                 // Запрещаем создание экземпляров.
  24.                 __allowInstantiation = false;
  25.             }
  26.             return __instance;
  27.         }
  28.         /**
  29.          * Конструктор.
  30.          */
  31.         public function Singleton()
  32.         {
  33.             if(!__allowInstantiation)
  34.                 throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте Singleton.instance.");
  35.         }
  36.     }
  37. }

В этой реализации паттерна в конструкторе делается проверка приватного свойства __allowInstantiation и создание экземпляра разрешается только в том случае, если значением __allowInstantiation является true. Совершенно понятно, что в статическом методе get instance мы кратковременно делаем это свойство равным true, а потом снова запрещаем создание экземпляра, делая свойство равным false.

Вариант поддерживает использование явного геттера get instance для обеспечения доступа к единственному экземпляру класса. Минус этой реализации в том, что класс хранит еще одно дополнительное свойство — __allowInstantiation.

Вариант с использованием вложенного класса

Извиняюсь за тавтологию, но без нее никак не обойтись. В ActionScript 3 в файлe класса можно помимо основного класса описывать еще и вложенные классы. К этим классам мы можем обращаться только из кода основного класса, и они не будут видимы за пределами основного класса. Находчивые люди решили использовать эту возможность для создания Одиночки:

Actionscript:
  1. package
  2. {
  3.     /**
  4.      * Пример реализации паттерна Одиночка на языке ActionScript 3
  5.      * с использованием вложенного класса.
  6.      *
  7.      * @author  Yuri "Barmaley" Yarovoy
  8.      * @version 1.0
  9.      */
  10.     public class Singleton
  11.     {
  12.         private static var __instance:Singleton;
  13.  
  14.         public static function get instance():Singleton
  15.         {
  16.             if(!__instance)
  17.             {
  18.                 __instance = new Singleton(new SingletonEnforcer());
  19.             }
  20.             return __instance;
  21.         }
  22.  
  23.         /**
  24.          * Конструктор.
  25.          *
  26.          * @param   enforcer   Параметр, гарантирующий вызов конструктора только внутри класса <code>Singleton</code>,
  27.          *       поскольку класс <code>SingletonEnforcer</code> не виден за пределами <code>Singleton</code>.
  28.          */
  29.         public function Singleton(enforcer:SingletonEnforcer)
  30.         {
  31.             if(enforcer == null)
  32.                 throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте Singleton.instance.");
  33.         }
  34.     }
  35. }
  36.  
  37. // Доступ к этому вложенному классу возможен только из класса Singleton,
  38. // поэтому никакой другой объект не сможет передать объект типа SingletonEnforcer в конструктор класса Singleton,
  39. // а, следовательно, и создать экземпляр класса Singleton при помощи конструктора.
  40. class SingletonEnforcer {}

Дополнительный параметр в конструкторе классаЕсли честно, то со стороны этот способ выглядит как банальный хак. В памяти программы теперь будет постоянно присутствовать SingletonEnforcer — пусть и сверхлегкий (без единого свойства и метода), но все-таки настоящий класс. Но самый большой недостаток этого варианта — это то, что в сигнатуре конструктора Singleton теперь присутствует совершенно непонятный со стороны параметр enforcer. При генерации документации к проекту, утилита ASDoc так же поместит описание этого параметра в документацию, обозначив его тип как SingletonEnforcer, но вот описание класса SingletonEnforcer в документацию помещено не будет. Вариант поддерживает использование явного геттера get instance для обеспечения доступа к единственному экземпляру класса.

Вариант с проверкой функции, создающей экземпляр класса

Встречается еще и следующая реализация паттерна:

Actionscript:
  1. package
  2. {
  3.     /**
  4.      * Пример реализации паттерна Одиночка на языке ActionScript 3
  5.      * с проверкой функции, создающей экземпляр класса.
  6.      *
  7.      * @author  Yuri "Barmaley" Yarovoy
  8.      * @version 1.0
  9.      */
  10.     public class Singleton
  11.     {
  12.         private static var __instance:Singleton;
  13.  
  14.         public static function getInstance():Singleton
  15.         {
  16.             if(!__instance)
  17.             {
  18.                 __instance = new Singleton(arguments.callee);
  19.             }
  20.             return __instance;
  21.         }
  22.  
  23.         public function Singleton(caller:Function)
  24.         {
  25.             if(caller != Singleton.getInstance)
  26.                 throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте Singleton.getInstance().");
  27.         }
  28.     }
  29. }

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

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

Actionscript:
  1. import Singleton;
  2. var s:Singleton = new Singleton(Singleton.getInstance);

Этот вариант выглядел бы гораздо привлекательней, если бы в ActionScript 3 поддерживалось свойство arguments.caller:

Actionscript:
  1. package
  2. {
  3.     /**
  4.      * Красивый вариант реализации паттерна Одиночка на языке ActionScript 3,
  5.      * который был бы возможен, если бы в этой версии языка не было устранено свойство <code>arguments.caller</code>.
  6.      *
  7.      * @author  Yuri "Barmaley" Yarovoy
  8.      * @version 1.0
  9.      */
  10.     public class Singleton
  11.     {
  12.         private static var __instance:Singleton;
  13.  
  14.         public static function getInstance():Singleton
  15.         {
  16.             if(!__instance)
  17.             {
  18.                 __instance = new Singleton();
  19.             }
  20.             return __instance;
  21.         }
  22.  
  23.         public function Singleton()
  24.         {
  25.             if(arguments.caller != Singleton.getInstance)
  26.                 throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте Singleton.getInstance().");
  27.         }
  28.     }
  29. }

Но, увы, этой возможности мы не имеем — свойство arguments.caller было устранено в AS3.

В-третьих, этот вариант реализации паттерна не позволяет использовать для доступа к единственному экземпляру класса явный геттер, поддерживаемый языком — get instance — поскольку следующая строка вызовет ошибку на этапе компиляции из-за несоответствия типов:

Actionscript:
  1. if(caller != Singleton.instance)

Подводим итоги

Когда я искал информацию по этой теме, то в комментариях одного англоязычного блога я встретил мнение, что если язык не поддерживает какие-либо конструкции, требуемые для реализации паттерна, то и не нужно ничего придумывать и обходными путями пытаться реализовать паттерн. Однако хоть реализация паттерна Singleton с приватным конструктором и является классической, однако она не навязывается ни одним сборником паттернов. Паттерны должны быть меньше всего привязаны к какому-то конкретному языку или его версии. Паттерны — это шаблоны дизайна приложения. Фактически это означает шаблон достижения в приложении того или иного функционала. А уж какую реализацию тот или иной паттерн имеет в конкретном языке — это дело второстепенное. Поэтому вывод тут должен быть только один: «Язык не поддерживает приватные конструкторы? Ищем альтернативу. Но, ни в коем случае не отказываемся от паттерна в том месте приложения, где он необходим».

Так как все из описанных в статье вариантов имеют те или иные минусы, то мы просто выберем из них один, имеющий меньшее количество недостатков. Варианты 3 и 4 с добавлением параметра в конструктор класса сразу же отметаются, так как они портят сигнатуру конструктора и могут ввести в замешательство другого разработчика, не знакомого с подобной техникой. Из оставшихся двух вариантов, вариант с инициализацией единственного экземпляра на этапе инициализации класса тоже выбывает, так как он не поддерживает ленивую инициализацию и может привести к сбоям в приложении, если ему вдруг потребуется использовать какие-то зависимости от других объектов. Остается вариант с приватным флагом, который и признается самым предпочтительным, работоспособным и безопасным. Все-таки, постоянно присутствующая в памяти булева переменная — это не такая уж и большая плата за возможность пользоваться паттерном Singleton.

Спасибо, что дочитали статью до конца и проделали все умозаключения вместе со мной. Продуктивной вам разработки! =)

См. так же:

Теги:


39 комментариев к записи:

Vooparker [ 25 октября , 2007 в 09:05 ]

По поводу документации к Singleton'ам. Конструктор можно исключить из документации полностью с помощью @private:

Actionscript:
  1. /**
  2. * Конструктор.
  3. * @private
  4. */
  5. public function Singleton(enforcer:SingletonEnforcer)

Юрий Яровой [ 25 октября , 2007 в 13:42 ]

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

Rostislav Siryk [ 30 октября , 2007 в 22:14 ]

Юр, отличный обзор! Всем линковать однозначно.

А вот здесь:
>>В ActionScript 3 в файлe класса можно помимо основного класса описывать еще и вложенные классы.

Ты под вложеныыми классами имеешь в виду классы с доступом internal?

Юрий Яровой [ 30 октября , 2007 в 22:31 ]

Нет, еще один класс, который описан в файле основного класса. Теперь ActionScript 3 как Java позволяет в одном файле класса описывать несколько классов: один основной, чье имя совпадает с названием файла, и любое количество вложенных классов. Вот здесь более подробно об этом: Вспомогательные классы в ActionScript 3. Хотя название «вспомогательные классы» я считаю не верным. «Вложенные» — так их называют в Java. =)

__i [ 31 октября , 2007 в 02:17 ]

Простите меня, но это явно перебор) из мухи такого слона )

Юрий Яровой [ 31 октября , 2007 в 10:37 ]

Честно говоря, я бы не назвал это перебором. Просто более глубокий, чем принято, анализ темы. Нужно же было, наконец, в этом вопросе расставить все точки над «i». ;)

супермэн [ 31 октября , 2007 в 10:44 ]

Какие такие проблемы решает синглтон которые не могут быть решены статическими методами и свойствами (условно назовем "статическим" классом)?

Если вы часто ошибаетесь и создаете экземпляр "статического" класса - то это проблемы ваши а не языка.

Rostislav Siryk [ 31 октября , 2007 в 11:06 ]

>> Простите меня, но это явно перебор) из мухи такого слона )

И от кого я это слышу? От человека, который распечатал и склеил вручную схему всех классов Flex Framework! :-)

Юрий Яровой [ 31 октября , 2007 в 11:36 ]

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

Rostislav Siryk [ 31 октября , 2007 в 11:08 ]

Гм, мой пред-предыдущий коммент с кусками кода не прошел. Пробую еще раз:

>> Вот здесь более подробно об этом: Вспомогательные классы в ActionScript 3...

Ага, я все понял. Меня смутило неявное указание модификатора доступа для вспомогательных классов. Явное выглядело бы так:

Actionscript:
  1. package
  2. {
  3.     public class Visible
  4.     {
  5.         public function Visible()
  6.         {
  7.             trace("Visible contsructor");
  8.             new Hidden();
  9.         }
  10.     }
  11. }
  12. internal class Hidden
  13. {
  14.     public function Hidden()
  15.     {
  16.         trace("Hidden constructor");
  17.     }
  18. }

Или, если брать твой СинглтонЭнфорсер, то
class SingletonEnforcer {}

А ведь кроме дефолтного internal ни один другой модификатор доступа не подойдет, у всех ошибка на этапе компиляции.

Соре за занудство... :)

Юрий Яровой [ 31 октября , 2007 в 12:09 ]

супермэн, я уже описал свое видение этого вопроса. И, если честно, снова возвращаться к нему я не хочу. Очень жаль, что у меня так и не получилось доступно все изложить в том посте.

Vooparker [ 31 октября , 2007 в 12:11 ]

Рост, в данном случае я думаю что опускать идентификатор internal уместно и даже более — полезно, явное указание internal подразумевает доступ внутри пакета, а так как сам SingletonEnforcer в пакет не входит идентификатор может внесни некоторую неясность.

Юрий Яровой [ 31 октября , 2007 в 12:12 ]

Рост, твои два коммента Akismet почему-то принял за спам. Я удалил первый, чтобы не было дублирования.

__i [ 31 октября , 2007 в 12:53 ]

>>И от кого я это слышу? От человека, который распечатал и склеил вручную
>>схему всех классов Flex Framework! :-)
не "всех классов Flex Framework" а всего лиш эффектов и твинов, там всего каких-то полсотни классов :Р

За то после Sparx EA видны все привано/протектед/паблик методы

a_[w] [ 31 октября , 2007 в 12:55 ]

Большое спасибо! Порадовали, особенно метод со вложенным классом. руль! ;)

__i [ 31 октября , 2007 в 13:22 ]

супермэн, синглтон вполне оправдывает свое применение

Например у нас с Reijii в часто в проектах существует только один класс которые отвечает за храниние DataStorage, получение и отсылку данных на сервер. Он имплементит несколько интрефейсов, грубо говоря для каждого "контроллера".
В теории можно было фигашить по классу с данными для каждого контролеера, но есть проблема - они подерутся за сервер (будут коллизии отправки данных и прочая херня) так вот чтобы избежать всего этого DataStorage делается синглтоном - т.е. в системе может, вернее должен быть всего 1 класс который отвечает за общение с сервером. Контроллеры грубоговоря "инитят" свой конкретный датасторадж, не заморачиваясь на сервер.

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

Юрий Яровой [ 31 октября , 2007 в 16:39 ]

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

Rostislav Siryk [ 1 ноября , 2007 в 11:25 ]

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

Dan [ 1 ноября , 2007 в 13:37 ]

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

Насчёт Синглтон vs Статические методы.

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

Например, если у нас есть класс, com.example.utils.StringUtils, то в байт-коде вызов метода этого класса быдет выглядеть примерно так:

- сунуть в стек "com"
- взять переменную
- сунуть в стек "example"
- взять поле
- сунуть в стек "utils"
- взять поле
- сунуть в стек "StringUtils"
- взять поле

и только уже после всего этого:
- сунуть в стек имя метода
- вызвать метод

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

В случае с Синглтоном там достаточно выполнить этот длинный ритуал один раз (при вызове getInstance), сохранить ссылку на экземпляр и затем ей пользоваться. Разумеется, ссылку лучше сохранять не в статичном поле, а то будет такая же свистопляска.

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

Vooparker [ 1 ноября , 2007 в 14:02 ]

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

Этот момент я не понял, то есть предлагается вместо

Actionscript:
  1. private static var __instance:Singleton;

использовать

Actionscript:
  1. private var __instance:Singleton;

Но каким образом тогда получить доступ к __instance через статичный метод? это просто не возможно, или я не уловил смысла :)

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

Вот здесь вообще ничего не понял, можно пример кода?

black [ 1 ноября , 2007 в 15:51 ]

Я так понимаю, что все эти варианты кода служат для маскировки очевидного бага фишки в реализации actionscript3 - отсутствия возможности создать private constructor. Варианты с выбрасыванием исключения из конструктора (когда его вызывают неправильным образом) - штука хорошая, но я, как человек взращенный на хлебах java, предпочел бы, чтобы неверное использование класса отбрасывалось бы на стадии компиляции кода. Иначе вполне возможно в большом проекте не покрытом качественно тестами пропустить места, где был по-глупости/невнимательности вызван конструктор и после сдачи в эксплуатацию кода получить "щелчок по носу".
Меня же больше напугала фраза "Но, увы, этой возможности мы не имеем — свойство arguments.caller было устранено в AS3." - я как-то пропустил ее при чтении дока. Получается что единственный способ узнать stack trace вызовов, это что-то вроде ???:

Actionscript:
  1. var trace: String;
  2. try
  3. {
  4.      throw new Error();// бросаем исключение
  5. }
  6. catch ( eee: Error )// ловим
  7. {
  8.      trace = eee.getStackTrace();
  9.      // парсим, и делаем что-то еще, например, пишем в лог
  10. }

Vooparker [ 1 ноября , 2007 в 17:11 ]

2Dan: походу я упустил момент, судя по тому коду который ты приводишь, ты пишешь про as2? Если да, тогда мне совсем не понятны все эти кульбиты, в as2 проблем с синглетонами не было благодаря приватным конструкторам :)

iv [ 1 ноября , 2007 в 16:29 ]
Actionscript:
  1. package {
  2.    
  3.     public class Singleton {
  4.        
  5.         private static const instance:Singleton = new Singleton();
  6.        
  7.         public static function getInstance ():Singleton {
  8.             return instance;
  9.         }
  10.        
  11.         private var __value:Number=0;
  12.        
  13.         public function Singleton () {}
  14.        
  15.         public function doSomething(num:Number):void {
  16.             instance.__value+=num;
  17.         }
  18.         public function get value():Number {
  19.             return instance.__value;
  20.         }
  21.        
  22.     }
  23. }

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

Dan [ 1 ноября , 2007 в 16:30 ]

2Vooparker

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

Например:

Actionscript:
  1. import com.example.utils.Singleton;
  2.  
  3. class com.example.test.SingletonUser {
  4.    static var singleton:Singleton;
  5.    
  6.    function init() {
  7.       // тормознутый вызов статического метода 
  8.       // плюс тормознутое обращение к статическому полю
  9.       singleton = Signleton.getInstance();
  10.  
  11.    }
  12.  
  13.    function doSomething() {
  14.       // тормознутое обращение к статическому полю
  15.       signleton.foo();
  16.    }
  17. }

Т.е. в этом случае для поля лучше убрать static.

По поводу хака. Вот пример.
Вспомогательный класс со статическими методами:

Actionscript:
  1. class com.example.foo.bar.Utils {
  2.     static function hello() {
  3.        trace("Hello");
  4.     }
  5. }

Класс, который сохраняет прямую ссылку для более быстрого вызова методов.

Actionscript:
  1. class Application {
  2.     private var utils;
  3.  
  4.     function Application() {
  5.         // сохраняем ссылку на класс
  6.         utils = com.example.foo.bar.Utils;
  7.     }
  8.  
  9.     function start() {
  10.         // используем её для быстрого вызова метода
  11.         utils.hello();
  12.     }
  13. }

Переменная utils нетипизированная. Можно, конечно, дать ей тип Object или Function - компилироваться будет, но нам это мало поможет.
Хотя можно использовать ещё более крутой хак.

Делаем интерфейс:

Actionscript:
  1. interface IUtils {
  2.    function hello();
  3. }

Utils его НЕ реализовывает, поэтому остаётся таким же, как выше.
А Application меняем вот так:

Actionscript:
  1. class Application {
  2.     // теперь переменная типизирована, и IDE должна правильно подставлять методы.
  3.     private var utils:IUtils;
  4.  
  5.     function Application() {
  6.         // обманываем компилятор
  7.         utils = Object(com.example.foo.bar.Utils);
  8.     }
  9.  
  10.     function start() {
  11.         utils.hello();
  12.     }
  13. }

Denis Kolyako [ 1 ноября , 2007 в 17:31 ]

Dan, про com.example.utils.StringUtils ты не прав, в байткоде там совершенно иначе все построено. «com.example.utils.StringUtils» не является объектом, это неймспейс.
Мы говорим исключительно про AS3.

Vooparker [ 1 ноября , 2007 в 17:06 ]

2Dan: вот все равно не понял, чем предложенные вами варианты лучше.
Касательно ссылки на Singleton в классе его использующем. Зачем создавать еще одно свойство класса для хранения ссылки на синглетон, когда можно отовсюду обращаться к синглетону через единую точку доступа — getInstance?
Касательно ссылки на класс. Опять же не понял какая выгода? Если у Utils есть статичный метод hello(), зачем устраивать такие присидания:

Actionscript:
  1. var utils = Utils;
  2. utils.hello();

когда можно просто вызвать:

Actionscript:
  1. Utils.hello();

И последнее про обман компилятора и интерфейс, в strict режиме это не прокатит, а если даже без strict режима мы все равно получим runtime ошибку.

iv [ 1 ноября , 2007 в 18:22 ]

ошибка закралась в код:
после строки
public static function getInstance():Singleton
нужна открывающаяся фигурная скобка: {

iv [ 1 ноября , 2007 в 17:55 ]

Вариант с использованием internal класса - просто гениален.
респект тому, кто эту фишку нашел.
я бы предложил этот вариант дополнить применением константы для instance
и не использовать геттер, поскольку это не является стандартом синглтона.
итог:

Actionscript:
  1. package {
  2.  
  3.     public class Singleton {
  4.  
  5.         private static var instance:Singleton = new Singleton(new SingletonEnforcer());
  6.  
  7.         public static function getInstance():Singleton
  8.             return instance;
  9.         }
  10.         public function Singleton(enforcer:SingletonEnforcer) {
  11.             if(enforcer == null) {
  12.                 throw new Error("Вы не можете создавать экземпляры класса при помощи конструктора. Для доступа к экземпляру используйте Singleton.instance.");
  13.             }
  14.         }
  15.     }
  16. }
  17. internal class SingletonEnforcer {}

Dan [ 1 ноября , 2007 в 18:59 ]

2Vooparker, 2Denis Kolyako

Я всё написал в своём первом сообщении: и про то, что я говорю именно про AS2, и про то, какие выгоды несут подобные извращения (именно в AS2).

Вообще я тут пишу не на тему "Как сделать Singleton на AS3", а на тему "Чем Singleton лучше статических классов".

Просто в труднее найти собеседников :)

__i [ 1 ноября , 2007 в 19:49 ]

А тут уже точно слон, народ че вы курите?

Юрий Яровой [ 5 ноября , 2007 в 14:33 ]

2 Dan:

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

Это не притязания на авторство, а простая автогенерация кода классов в FlashDevelop. По идее, конечно же, нужно было бы удалить тэги @author и @version из листингов, но я попросту забыл это сделать. =)
2 black:

Я так понимаю, что все эти варианты кода служат для маскировки очевидного бага фишки в реализации actionscript3 - отсутствия возможности создать private constructor.

и

Меня же больше напугала фраза «Но, увы, этой возможности мы не имеем — свойство arguments.caller было устранено в AS3.»

Да, меня тоже не сильно обрадовали эти нововведения в языке.
2 iv:

Вариант с использованием internal класса - просто гениален.
респект тому, кто эту фишку нашел.

Он оригинален, но добавляет в конструктор дополнительный параметр, который нужен только для того, чтобы выполнить проверку на допустимость создания экземпляра. Мне кажется, что нужно избавляться от таких непонятных параметров в коде.
2 __i:

А тут уже точно слон, народ че вы курите?

Почему слон? Тут хоть наконец-то полностью разобрались в этом вопросе. =)

iv [ 21 января , 2008 в 14:48 ]

в итоге я всё больше склоняюсь к вот такому варианту:

Actionscript:
  1. public static const INSTANCE : Singleton = new Singleton();
  2. public function Singleton () {
  3.     if (INSTANCE) throw new Error("**Error** - singleton. Сonstructor is locked.");
  4. }

плюсы:
1. Ленивая инициализация
Опасения на счет массовой инициализации констант INSTANCE на старте приложения не оправданы. Класс будет проинициализирован при первом обращении к нему. И не помню случая, чтобы что-то требовалось в синглтоне без обращения к экземпляру, если такое произойдет, то проблема скорее в неверной архитектуре, а не в константе. И, даже если такое произойдет, то ничего страшного. Такой случай - редкость и беспокоиться о том, что экземпляр синглтона создался на пару кадров раньше - абсурд.

2. Быстрота
Все проверки кроме одной выполняются на этапе компиляции. Вы не сможете случайно присвоить другое значение константе, или нарваться на ситуацию, когда константа пуста.
А проверка в конструкторе класса будет произведена лишь единожды. Любая попытка сделать это не единожды приведет к тому, что придется переписать код.
Доступ через метод getInstance() с его постоянной проверкой if(instance==null) в этом смысле проигрывает по всем статьям. Мелочь, но всё-же.

3. Отсутствие лишнего и простота рефакторинга
Этот способ не использует SingletonEnforcer (хотя сама идея мне очень нравится) и причина тому выше - только один раз нужно сделать проверку на создание экземпляра и не стоит ради этого городить огород.
Код для обслуживания синглтона очень короткий, поэтому, при необходимости, рефакторить практически нечего.

Недостатки:
Несоответствие стандарту шаблона Singleton. Частично компенсируется п.3 достоинств.

Юрий Яровой [ 22 января , 2008 в 03:37 ]

Если честно, Ив, то в последнее время я тоже склоняюсь к использованию Одиночки, создаваемого при помощи статической константы. Уж очень его в таком виде быстро и просто создавать. И ошибку при создании практически невозможно допустить. Единственное, я пишу instance в нижнем регистре.

iv [ 22 января , 2008 в 12:20 ]

Я предпочитаю оставаться в рамках соглашения об именовании. Необязательность применения соглашения приводит к ухудшению читабельности кода.

Рост [ 22 января , 2008 в 13:23 ]

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

Юр — какая продуктивная тема оказалась-то.

Юрий Яровой [ 22 января , 2008 в 13:38 ]

Я предпочитаю оставаться в рамках соглашения об именовании. Необязательность применения соглашения приводит к ухудшению читабельности кода.

Тоже верно, не спорю. Но нижний регистр в моем случае — это просто дань традиции. Привычка. =)

iv [ 22 января , 2008 в 13:49 ]

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

Actionscript:
  1. public static var _instance : Singleton;
  2. public static function get instance ():Singleton {
  3.     if (!_instance) {
  4.         _instance = new Singleton();
  5.     }
  6.     return _instance;
  7. }

работает без проблем.

dimpiax [ 10 августа , 2008 в 18:49 ]

Я предпочитаю такую реализацию единственного экземпляра класса.

Actionscript:
  1. ...
  2. private static var __called : Boolean;
  3. private static var __instance : Class;
  4.  
  5. public static function get instance() : Class {
  6.     Class.__called = true;
  7.     if(Class.__instance == null) Class.__instance = new Class();
  8.     return Class.__instance;
  9. }
  10.  
  11. public function Class() {
  12.     if(__instance) throw Error('This class uses Singelton pattern.');
  13.     else if(!__called) throw Error('You must access to class by Class.instance');
  14. }

[...] «Варианты реализации паттерна Singleton в языке ActionScript 3.... [...]

Оставьте свой комментарий:

Имя: *
* — обязательно для заполнения
Электропочта: *
Сайт:
Сообщение *
Коментировать
Коментировать