Pull to refresh

Type-Safe Enumerations в Action Script 3

Reading time3 min
Views2K
Готовлю статью про данный паттерн в Java. Нашёл необходимый материал и понял, что статья получиться довольно объёмная и сходу написать её не получиться. Поэтому решил написать про этот паттерн во Flash и глянуть на реакцию сообщества.

Перечисление (Enumerations) это множество близких по смыслу значений (элементов). Например:
  • Направления света — север (north), юг (south), восток (east), запад (west)
  • Тип романа — mystery, classic, fantasy, romance, science-fiction
  • flavours of ice cream — chocolate, vanilla, raspberry, maple


Ранее стандартным решением для представления перечисляемых типов использовался int Enum паттерн:

    // int Enum Pattern - has severe problems!
    public static const SEASON_WINTER : Number = 0;
    public static const SEASON_SPRING : Number  = 1;
    public static const SEASON_SUMMER : Number  = 2;
    public static const SEASON_AUTUMN : Number = 3;

Этот паттерн имеет много недостатков. Попытаемся перечислить некоторые:
  • Not typesafe — В тех местах, где ожидается season можно втавить любое другое int значение, или, к примеру сложить два сезона, что не имеет смысла.
  • No namespace — Необходимо использовать префикс для констант (в нашем примере SEASON_) для того, чтобы избежать колизий с другими наборами констант.
  • Printed values are uninformative — Из-за того, что значения констант являются просто целочисленными значениями, то при их выводе на консоль (экран, файл и т.д.) будет отображено просто значение, которое ни чего не скажет о предназначении данной константы и о её типе.

Суть данного паттерна заключается в следующем:
  • создаются статические константы определённого типа (Enum type)
  • Конструктор данного типа принимает значение, которое он будет оборачивать
  • У данного типа должен быть метод, который вернет оборачиваемое значение

Пример для наших сезонов может выглядеть следующим образом:

package
{
	/**
	 * Пример реализации паттерна TypeSave Enumeration на языке ActionScript 3.
	 *
	 * @author  Aleh Krutsikau
	 * @version 0.1
	 */
	public class Season
	{
		// Ссылка на единственный экземпляр класса.
		public static const WINTER : Season = new Season("winter");
		public static const SPRING : Season = new Season("spring");
		public static const SUMMER : Season = new Season("summer");
		public static const AUTUMN : Season = new Season("autumn");

		private static var _enumCreated:Boolean = false;
		// magic happens here, the static code block
		{
			_enumCreated = true;
		}

		private var _seasonName : String;

		/**
		 * Конструктор.
		 */
		public function Season(seasonName : String)
		{
			if (_enumCreated)
				throw new Error("The enum is already created.");
			_seasonName = seasonName;
		}

		public function get seasonName() : String {
			return _seasonName;
		}
	}
}


Пример использования

// Пример передачи значения типизированного перечисления
public function traceSeason(season : Season) {
	trace(season.seasonName);
}

traceSeason(Season.WINTER);


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

Стоит только отметить, что в классической реализации данного паттерна конструктод должен быть private. Но увы, ActionScript 3.0 этого не поддерживает.Конечно, можно было использовать вложенный класс. Но, на мой взгляд, этот метод не сильно красив (более подробно можно глянуть в статье Варианты реализации паттерна Singleton в языке ActionScript 3).

Есть более «элегантное» решение (кстати оно и приведено в примере). AS3 вводит понятие блока статической инициализации. Код, который расположен в этом блоке исполняется в момент загрузки данного класса в память. Это происходит до того как вызывается конструктор, но, при этом, после того, как инициализируются все статические переменные класса. Таким образом, заведя флаг разрешения создания экземпляра класса, мы можем ограничить доступ к конструктору. Опишем порядок выполняемых при этом действий:
  1. В память загружается наш класс
  2. Происходит инициализация статических переменных.
  3. _enumCreate становится равным false (значение по умолчанию)
  4. Создаётся первая статическая константа WINTER. Т.к. _enumCreate равно false, то экземпляр данного класса прекрасно создаётся.
  5. Создаётся вторая ....
  6. Происходит выполнения блока статической инициализации, в котором переменной _enumCreated
    присваивается значение true. Тем самым исключается возможность «ручного» создания экземпляра класса Season
  7. Та-да. Класс загружен :)

И еще, если вы хотите, чтобы при trace(Season) выдавалось что то осмысленное, то просто перегрузите метод toString().

Источники:
Tags:
Hubs:
+1
Comments14

Articles