2012-02-29

Overload (перегрузка) посредством Namespace в ActionScript

Снова лирическое отступление или мне начинает казаться, что я пишу мемуары, а не статьи о программировании...

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

После Delphi и Visual Basic писать на Java было приятней, язык оказался плотно завязан на ООП (то есть о функциональном программировании и речи быть не могло). Здесь все удобно получалось разложить по полочкам и, ввиду того, что стандартные интерфейсы, предоставляемые MIDP (mobile information device profile) меня не устроили, все элементы пользовательского интерфейса разрабатывались мною самостоятельно и описывались классами с наследованиями от базовых до конкретных представлений.

Сейчас, более плотно занявшись ActionScript, я понял, что мне не хватает некоторого функционала, что был в Java. Есть такое понятие "overloading" (перегрузка), то есть мы можем иметь сколь угодно много функций с одинаковым именем, но разными аргументами. Очень удобная и часто используемая мной фишка языка, к сожалению не присутствует в ActionScript, хотя могла бы существовать, ведь это язык со строгой типизацией. Зато в ActionScript есть "namespace", с помощью которых мы и попробуем организовать какое-то подобие overloading.


Задача такова

Имеется словарь с какими-то списочными данными. В разных случаях нам нужно передавать в него аргументы и получать результаты разных типов.

Более конкретно: у нас есть список объектов, где он должен быть представлен просто массивом, а где-то вектором. Называть функции по типу getListArray или getListVector мне не нравится, лучше используем namespace.

Смотрим что получилось.

Dic.as

package  
{
 import flash.utils.Dictionary;
 
 public class Dic 
 {
  public namespace arr;
  public namespace vec;
  private var _list:Array;
  
  public function Dic() {}
  
  arr function get list():Array
  {
   return _list;
  }
  
  vec function get list():Vector.<String>
  {
   var v:Vector.<String> = new Vector.<String>;
   for each (var obj:String in _list) {
    v.push(obj);
   }
   return v;
  }
  
  arr function set list(a:Array):void
  {
   _list = a;
  }
  
  vec function set list(v:Vector.<String>):void
  {
   _list = new Array;
   for each (var obj:String in v) {
    _list.push(obj);
   }
  }
 }
}

Main.as

package 
{
 import flash.display.Sprite;
 import flash.utils.getQualifiedClassName;
 
 public class Main extends Sprite 
 {
  public function Main():void 
  {
   var dic:Dic = new Dic();
   
   // получим пространства имен из Dic
   var arr:Namespace = Dic.arr;
   var vec:Namespace = Dic.vec;
   
   // инициализируем тестовые данные
   var list:Array = [ 'a', 'b', 'c' ];
   var vector:Vector.<String> = new Vector.<String>;
   vector.push( 'd' );
   vector.push( 'e' );
   vector.push( 'f' );
   
   // заполним список с помощью Array
   dic.arr::list = list;
   // получим список в виде Vector.<String>
   trace(getQualifiedClassName(dic.vec::list), ' = ', dic.vec::list);
   
   // заполним список с помощью Vector.<String>
   dic.vec::list = vector;
   // получим список в виде Array
   trace(getQualifiedClassName(dic.arr::list), ' = ', dic.arr::list);
   
  }
 }
}

Как вариант, установку значений в list можно было реализовать без namespace`ов, всего лишь указав "*" в качестве принимаемого типа аргумента и внутри функции проверять тип. В get-функциях, в зависимости от требований задачи, в некоторых случаях можно тоже не использовать namespace, если, к примеру, тип возвращаемого значений зависит от типа входных параметров. Так что необходимо думать и грамотно выбирать места, где удобно использовать namespace, а где они не нужны вовсе.