User's collector

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

Я уже давно подписан на RSS блога «Flash-разработка», поскольку нахожу публикуемые там материалы интересными, актуальными и, порой даже, — уникальными. Совсем недавно на этом ресурсе была опубликована серия статей под общим заголовком «Хороший стиль Flex-программирования», являющаяся русскоязычным переводом документа «Flex SDK coding conventions and best practices» — соглашения по написанию кода в классах Flex SDK. Этот документ примечателен тем, что он может помочь не одной команде разработчиков прийти к общему стилю создаваемого ими кода.

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

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

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

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

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

Но вернемся к теме сегодняшней статьи. Я хотел бы сказать, что команды Flex и ActionScript разработчиков могут полностью принять соглашения по написанию кода в классах Flex SDK, предлагаемые программистами Adobe, или взять их за основу, при создании своих собственных соглашений. Тем более что сейчас этот документ полностью доступен на русском языке. Объем переведенного текста достаточно большой, и за проделанную работу я выражаю автору огромную благодарность. Ну и в качестве заключительного аккорда предлагаю ссылки на все статьи из этой серии:

P.S.: Хотелось бы видеть все эти части собранными в один документ. =)

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 комментариев

Теги:

Наверняка ведь многие задавались вопросом: «Почему бы не использовать вместо Одиночки обычный класс со статическими методами и свойствами?». В ActionScript 3 создать Singleton без дополнительных маневров не получится и бывают случаи, когда хочется попросту обойтись без них.

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

  1. В ActionScript 3 статические методы и свойства не наследуются. Т. е. если вы реализовали какой-либо функционал в виде статических членов класса, то он не будет передан по наследству подклассам класса и вам придется заново определять этот функционал в каждом из подклассов.
  2. В дальнейшем ходе развития кода вашего приложения вы можете пересмотреть политику взаимодействия объектов в нем. И вам может понадобиться уже несколько экземпляров вашего класса, каждый из которых будет хранить собственные значения свойств, обладая при этом схожим с другими экземплярами функционалом. В случае использования Одиночки это делается без особого труда — достаточно переделать статический метод getInstance так, чтобы он каждый раз возвращал новый объект класса. Есть даже специальное название для паттерна, обладающего с Singleton схожей структурой, но позволяющего создавать несколько экземпляров классов и вести их учет — Multiton. В случае же использования класса со статическими членами это сделать уже довольно сложно: пришлось бы создавать полностью одинаковые классы-дубликаты нашего класса, что само по себе является просто ужасным примером дизайна приложения.
  3. Значения статическим членам класса будут присвоены во время инициализации самого класса. На практике это означает, что, как только дойдет очередь до инициализации класса, AVM или JVM присвоит статическим свойствам этого класса необходимые значения, и с этого момента они будут постоянно висеть в памяти, занимая и растрачивая попусту ресурсы компьютера. Это может быть особенно заметно, если класс хранит достаточно увесистые объекты типа изображений, звуков или видео. Кроме того потребуется больше времени и на запуск приложения. В отличие от этого варианта, вы можете разработать ваш Singleton таким образом, чтобы он поддерживал «ленивую» инициализацию. «Ленивой» называется создания экземпляра класса и присваивание значений его свойствам только тогда, когда это необходимо на самом деле, т.е. во время первого вызова getInstance.
  4. Использование объектов — более легкий вариант для понимания и отладки. Использование статических членов класса, которые не являются частью какого-то простого объекта, а принадлежат к самому классу, означает, что ваш код не создается в определенной точке вашего приложения. В результате этого могут возникать ошибки, которые довольно сложно выявить. Главным образом это проявляется, если ваш класс должен содержать ссылки на другие объекты, которые попросту еще не будут доступны во время инициализации свойств класса.

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

При создании классов утилит, т. е. классов представляющих собой библиотеки связанных друг с другом статических методов и статических полей, предназначенных для обработки простых значений или выполнения простых задач, не требующих взаимодействия с другими объектами. Примерами таких классов являются Math в ActionScript 3 или java.util.Arrays в Java. Создание экземпляров таких классов являлось бы абсурдным. Так же, маловероятно, что когда-нибудь потребуется наследоваться от этих классов. И, как правило, классы утилит не содержат большого количества полей и методов, чтобы это заметно сказывалось на времени запуска приложения. Поэтому в данном случае предпочтительнее использование класса со статическими членами вместо Одиночки.

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

Actionscript:
  1. package
  2. {
  3.     public class Foo
  4.     {
  5.         public function Foo()
  6.         {
  7.            throw new Error("You can't create instance of Foo. All its methods and properties are static");
  8.         }
  9.     }
  10. }

В ActionScript 2 или Java используйте простой приватный конструктор.

P.S.: Обязательно прочтите статью «Работа с сookies в as3 приложениях» от Алексея «Vooparker» Аникутина, в которой как раз и приводится пример небольшого, но очень полезного утилитного класса.

См. так же:

10 комментариев

Теги:

Наверно, как и большинство из вас познакомившись с объектно-ориентированным подходом в программировании, я стал видеть классы всюду и везде, причем порой, даже там, где в них и не было необходимости. Очень быстро мои приложения стали просто изобиловать классами, которые в большинстве своем не сильно отличались друг от друга (если вообще отличались). Сегодня я предлагаю поговорить о средстве, которое гарантированно поможет вам избавиться от «лишних» классов и в тоже время внесет стройности в ваши приложения. Безусловно, я уверен, что среди читателей Garbage Collector’а есть те, кто знаком с практикой описанной в этой статье (от них я жду комментариев), но также уверен, что кому-то этот материал окажется новым и полезным. Ну вот, со вступлением покончено, теперь в бой.
За окном осень вот-вот вступит в свои права, так что пример я предлагаю взять осенний — программа, имитирующая листопад. В задачи программы входят генерация листьев и последующее управление ими. Так как все листья обладают схожими качествами, мы определяем базовый класс Leaf, описав в нем вид дерева, которому лист принадлежит, размер и оттенок (методы и прочие свойства я показывать не буду):

Actionscript:
  1. package
  2. {
  3.     public class Leaf
  4.     {
  5.         public var type:String;  
  6.         public var size:String;
  7.         public var tint:String;
  8.        
  9.         public function Leaf (type:String, size:String, tint:String)
  10.         {
  11.             this.type = type;
  12.             this.size = size;
  13.             this.tint = tint;
  14.         }
  15.        
  16.         // прочие методы и свойства
  17.     }
  18. }

Теперь мы знаем, что наш листопад происходит в лесу, где растут, например, клены и березы. А так же мы знаем, что кленовые листья большие и темные, а березовые – маленькие и светлые. То есть параметр type определяет остальные два параметра. Логичным на первый взгляд, кажется скрыть эту зависимость в подклассах Leaf: MapleLeaf и BirchLeaf:

Actionscript:
  1. package
  2. {
  3.     public class MapleLeaf extends Leaf
  4.     {
  5.         public function MapleLeaf ()
  6.         {
  7.             super('maple', 'big', 'dark')
  8.         }
  9.     }
  10. }

Actionscript:
  1. package
  2. {
  3.     public class BirchLeaf extends Leaf
  4.     {
  5.         public function BirchLeaf ()
  6.         {
  7.             super('birch', 'small', 'light');
  8.         }
  9.     }
  10. }

А теперь давайте посмотрим на наши новые классы внимательно. Отличаются ли они чем-то от базового класса Leaf? В целом — нет, с точки зрения API это абсолютно идентичные классы, если не считать создание экземпляров. К тому же с таким «классовым» подходом в случае добавления нового вида листьев нам придется вводить каждый раз новый подкласс Leaf. А если вы захотите добавить интерактивности в приложение в виде возможности пользователю самому создавать новые виды листьев с произвольными значениями своих свойств на этапе выполнения приложения, тогда мы просто не можем создать новый класс? Думаю, негибкость создания подклассов в приведенном примере очевидна.

И так нам нужен способ избавиться от лишних классов, получить удобный способ инстанцирования новых объектов, а также формирование новых «видов» объектов на этапе выполнения, ну и конечно возможность использовать наше решение повторно. Решить все эти задачи нам поможет паттерн проектирования Прототип (Prototype), суть которого заключена в создании новых экземпляров класса, обладающих необходимыми характеристиками, путем клонирования уже существующих экземпляров (прототипов) с заранее заданными характеристиками.

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

Как я уже сказал, паттерн основывается на клонировании существующих экземпляров. Таким образом, у любого прототипа должен присутствовать метод clone() который должен возвращать точную копию экземпляра. Т. к. каждый клонируемый объект обладает свойственной его классу структурой, то создать базовый класс с реализованным в нем методом clone() не получится. Да и к тому же вполне возможно, что класс, который вы собираетесь наделить способностью клонироваться, уже от кого-то наследуется. Поэтому мы определяем интерфейс IPrototype (ЙаПрототип:)), который должны реализовывать все классы, экземпляры которых мы будем клонировать:

Actionscript:
  1. package
  2. {
  3.     public interface IPrototype
  4.     {
  5.         function clone ():IPrototype;
  6.     }
  7. }

Теперь модифицируем наш класс Leaf реализовав в нем метод clone():

Actionscript:
  1. package
  2. {
  3.     public class Leaf implements IPrototype
  4.     {
  5.         public var type:String;
  6.         public var size:String;
  7.         public var tint:String;
  8.  
  9.         public function Leaf (type:String, size:String, tint:String)
  10.         {
  11.             this.type = type;
  12.             this.size = size;
  13.             this.tint = tint;
  14.         }
  15.  
  16.         public function clone ():IPrototype
  17.         {
  18.             return new Leaf(type, size, tint);
  19.         }
  20.         // прочие методы и свойства
  21.     }
  22. }

Теперь мы можем создать «кленовый» экземпляр класса Leaf и в случае чего его клонировать:

Actionscript:
  1. var mapleLeaf:Leaf = new Leaf('maple', 'big', 'dark');
  2. var anotherMapleLeaf:Leaf = mapleLeaf.clone() as Leaf;

Так как возвращаемый методом clone() тип объявлен как IPrototype, мы приводим к нужному типу с помощью оператора as.

С первой задачей мы справились, у нас всего на всего один класс. Теперь реализуем удобный способ создания новых экземпляров на базе прототипов, а также возможность создавать и удалять прототипы на этапе исполнения. Для этого создадим класс PrototypeManager, который должен хранить в себе прототипы, предоставлять возможность регистрации и удаления прототипов, и главное, создавать новые экземпляры. Чтобы получить доступ к тому или иному прототипу, каждый из них будет иметь уникальный строковый идентификатор. И так, давайте посмотрим, как может выглядеть реализация PrototypeManager:

Actionscript:
  1. package
  2. {
  3.     public class PrototypeManager
  4.     {
  5.         private var __prototypes:Object;
  6.  
  7.         public function PrototypeManager ()
  8.         {
  9.             __prototypes = {};
  10.         }
  11.  
  12.         public function addPrototype (prototypeName:String, prototype:IPrototype, overridePrecursor:Boolean=false):Boolean
  13.         {
  14.             if(__prototypes[prototypeName] == undefined || overridePrecursor)
  15.             {
  16.                 __prototypes[prototypeName] = prototype;
  17.                 return true;
  18.             }
  19.             else
  20.             {
  21.                 return false;
  22.             }
  23.         }
  24.  
  25.         public function removePrototype (prototypeName:String):Boolean
  26.         {
  27.             if(__prototypes[prototypeName] != undefined)
  28.             {
  29.                 delete __prototypes[prototypeName];
  30.                 return true;
  31.             }
  32.             else
  33.             {
  34.                 return false;
  35.             }
  36.         }
  37.  
  38.         public function make (prototypeName:String):IPrototype
  39.         {
  40.             if(__prototypes[prototypeName] != undefined)
  41.                 return (__prototypes[prototypeName] as IPrototype).clone();
  42.             else
  43.                 return null;
  44.         }
  45.     }
  46. }

Как же работает PrototypeManager? А работает он просто. PrototypeManager хранит все прототипы в своем пуле под уникальными строковыми идентификаторами, при необходимости используя метод прототипа clone() создает его копию. Так же PrototypeManager предоставляет методы добавления, перезаписи и удаления прототипов хранящихся в пуле. Теперь давайте посмотрим на пример работы с PrototypeManager:

Actionscript:
  1. // Создаем экземпляр PrototypeManager
  2. var pm:PrototypeManager = new PrototypeManager();
  3.  
  4. // Добавляем прототипные экземпляры в пул менеджера под уникальными именами
  5. pm.addPrototype('mapleLeaf', new Leaf('maple', 'big', 'dark'));
  6. pm.addPrototype('birchLeaf', new Leaf('birch', 'small', 'light'));
  7.  
  8. // Создаем экземпляры прототипов
  9. var mapleLeaf:Leaf = pm.make('mapleLeaf') as Leaf;
  10. var birchLeaf:Leaf = pm.make('birchLeaf') as Leaf;

Сразу оговорюсь, реализована только минимально необходимая функциональность PrototypeManager все остальные «навороты» (события, предоставление списка прототипов и т.д.) вы можете дописать сами.

Каковы же результаты применения паттерна Прототип в нашем случае? Во-первых, мы ограничились всего одним действительно нужным классом Leaf, который обладает всем необходимым API, во-вторых, мы получили удобный способ создания экземпляра Leaf c заранее заданными параметрами, а также возможность добавить неограниченное количество прототипов на этапе выполнения. И, наконец, мы можем смело использовать IPrototype и PrototypeManager в следующих проектах. По-моему не плохо. А вы как считаете? ;)

P.S.: Самым важным сложным моментом для реализации паттерна Прототип является реализация метода clone(). В AS3 для клонирования объектов можно воспользоваться копированием через ByteArray. Однако мне такой метод не кажется удачным. Например, если в классе существует внутренний счетчик экземпляров, копирование через ByteArray приведет к сбою этого счетчика. Но в любом случае реализация метода clone() зависит от клонируемого.

3 комментария

Теги: