Март 2011
Руководство по actionscript. часть 3, стр. 012
Когда среда выполнения проверяет существование переменной description в открытых пространствах имен, она находит два соответствия: private : .-description и protected: :description класса Child. Как мы уже знаем из предыдущего раздела, когда неуточненная ссылка соответствует имени в более чем одном пространстве имен, возникает ошибка неоднозначного обращения. Более того, если несколько имен уточняются различными неявно открытыми пространствами имен, возникает ошибка, связанная с конфликтом определений. В случае с переменной description возникнет следующая ошибка:
A conflict exists with inherited definition Parent. description in namespace protected.
На русском языке она будет выглядеть так: Существует конфликт с унаследованным определением Parent. description в пространстве имен protected.
Если в вашем коде существуют конфликтующие имена методов и переменных, компилятор опишет суть конфликта, указав пространство имен, в котором этот конфликт произошел. Например, следующий код:
package { import flash. display.*; public class SomeClass extends Sprite { private var prop:int;
private var prop:int; // Недопустимое повторное определение свойства
}
}
вызовет следующую ошибку:
A conflict exists with definition prop in namespace private.
По-русски это будет звучать так: Существует конфликт с определением prop в пространстве имен private.
На самом деле из-за ошибки компилятора в приложениях Flex Builder 2 и Flash CS3 предыдущее сообщение будет содержать неправильную фразу namespace internal, хотя должно быть namespace private.
Подобным образом, данный код:
package { import flash. display.*; public class SomeClass extends Sprite { private var x;
вызовет следующую ошибку (поскольку — об этом вы можете почитать в справочнике по языку ActionScript компании Adobe — в классе Di splayOb j ect уже определена переменная х с использованием модификатора управления доступом public):
A conflict exists with inherited definition f1 ash. di splay:Di splayObject. x in namespace public.
Руководство по actionscript. часть 3, стр. 013
На русском языке это будет выглядеть следующим образом: Существует конфликт с унаследованным определением flash. display:DisplayObject. x в пространстве имен public.
Директива import открывает пространства имен public. Стоит отметить, что с технической точки зрения импортирование пакета, как показано в следующем коде:
import somePackage.*;
открывает пространство имен public импортированного пакета. Тем не менее оно не открывает пространство имен internal импортированного пакета. Даже если пакет импортируется, его идентификаторы, объявленные с использованием модификатора управления доступом internal, остаются недоступными для внешнего кода.
Практические примеры использования пространств имен
В самом начале этой главы упоминалось четыре практических сценария использования пространств имен:
? предотвращение конфликтов именования;
? управление видимостью членов на уровне прикладной среды;
? управление доступом на основании разрешений;
? реализация различных режимов работы программы.
В предыдущем разделе рассказывалось, как пространства имен предотвращают конфликты именования. В этом разделе мы рассмотрим каждый из трех оставшихся сценариев на примерах из реальной жизни.
Пример: управление видимостью на уровне прикладной среды
Наш первый пример прикладного использования пространств имен взят из прикладной среды Flex компании Adobe — это библиотека компонентов пользовательского интерфейса и утилит для разработки интернет-приложений с широкими функциональными возможностями.
Прикладная среда Flex включает большое количество кода — сотни классов, размещаемых в дюжинах пакетов. Некоторые методы и переменные этих классов должны быть доступны в различных пакетах, но при этом они должны считаться внутренними по отношению ко всей прикладной среде. Возникает дилемма: если методы и переменные объявить с использованием модификатора управления до-
ступом public, код, находящийся за пределами прикладной среды, будет иметь к ним нежелательный доступ, но если их объявить с использованием модификатора управления доступом internal, эти методы и переменные нельзя будет использовать в других пакетах.
Руководство по actionscript. часть 3, стр. 014
Для разрешения этой ситуации в прикладной среде Flex определяется пространство имен mx internal, используемое для уточнения методов и переменных, которые не должны быть видны за пределами прикладной среды, но при этом должны быть доступны в различных пакетах внутри ее.
Вот объявление пространства имен mx internal:
package mx. core { public namespace mx_internal =
«http://www. adobe. com/2006/f1ex/mx/i nternal»;
}
Рассмотрим конкретный пример использования пространства имен mx internal из прикладной среды Flex.
Для работы с табличными данными наподобие тех, которые используются в программах электронных таблиц, прикладная среда Flex предоставляет компонент DataGr id. Класс DataGrid находится в пакете mx. controls. Вспомогательные классы для компонента Dat aGr id размещаются в отдельном пакете:mx. controls. gridclasses. Чтобы взаимодействие между классом DataGrid и его вспомогательными классами осуществлялось максимально эффективно, DataGrid обращается к некоторым внутренним переменным его вспомогательных классов напрямую, а не с помощью доступных всем методов-получателей. Однако эти внутренние переменные не должны использоваться классами за пределами прикладной среды Flex, поэтому они уточняются пространством имен mx internal. Например, вспомогательный класс mx. controls. gridclasses. DataGridColumn хранит индекс столбца в переменной mx_internal: : colNum.
// Файл DataGridColumn. as mx_internal var colNum:Number;
Чтобы получить индекс столбца, класс DataGrid сначала открывает пространство имен mx_internal:
use namespace mx_internal;
а затем обращается к переменной mx internal: : colNum напрямую, как показано в следующем фрагменте кода, взятого из определения метода-писателя:
// Файл DataGrid. as
public function set columns(value:Array):void { // Инициализируем «colNum» для всех столбцов var n:int = value. length; for (var i:int = 0; i < n; i++) {
var column:DataGridColumn = _columns[i];
column. owner = this;
// Обращаемся к переменной mx_internal::colNum напрямую. (Напомним, что
// пространство имен mx_internal открыто, поэтому выражение
// column. colNum эквивалентно выражению column. mx_internal: .colNum.)
column. colNum = i;
}
// Оставшаяся часть метода не приводится
}
Классы за пределами прикладной среды Flex для получения индекса столбца используют общедоступный метод getColumnlndex ( ) вместо обращения к переменной mx_internal: : colNum напрямую.
Руководство по actionscript. часть 3, стр. 015
Проблема решена на 9/10. Помещение переменных и методов в пространство имен mx internal, безусловно, уменьшает их непосредственную видимость, однако технически это не запрещает коду за пределами прикладной среды Flex обращаться к ним. Любой разработчик, которому известен идентификатор URI пространства имен mx_internal, может применять этот идентификатор для обращения к любой переменной или методу, уточняемому с использованием пространства имен mx_internal.
Однако целью пространства имен mx internal является не техническое запрещение использования переменных и методов разработчиком. Скорее оно является большим знаком предупреждения, сообщающим о том, что переменные и методы не предназначены для использования внешним кодом и могут быть изменены без предупреждения или привести к ошибочному поведению, если обращение к ним осуществляется из кода за пределами прикладной среды Flex.
Пример: управление доступом на основании разрешений
Второй пример использования пространств имен демонстрирует вариант механизма управления доступом, когда класс определяет группу методов и переменных, к которым могут обращаться только определенные классы. В этом примере задействованы следующие участники.
Защищаемый класс — класс, который предоставляет доступ к своим защищенным методам и переменным.
Методы и переменные с ограниченным доступом — группа методов и переменных, доступ к которым ограничен.
Авторизованные классы — классы, которым разрешен доступ к методам и переменным с ограниченным доступом.
Рассмотрим базовый код защищаемого класса:
package { // Это защищаемый класс, public class ShelteredClass {
// Пространство имен restricted уточняет переменные
// и методы, доступ к которым ограничен.
private namespace restricted;
// Это массив авторизованных классов. В данном примере // определен только один авторизованный класс: Caller, private var authorizedClasses:Array = [ Caller ];
// Это переменная с ограниченным доступом.
// К ней могут обращаться только авторизованные
// классы.
Руководство по actionscript. часть 3, стр. 016
Restricted var secretData:String = «No peeking»;
// Это метод с ограниченным доступом.
// К нему могут обращаться только авторизованные
// классы.
restricted function secretMethod ( ).void { trace(«Restricted method secretMethod( ) called»);
}
}
}
Защищаемый класс хранит массив авторизованных классов. Кроме того, в нем объявлено пространство имен с использованием модификатора управления доступом private, которое применяется для уточнения методов и переменных с ограниченным доступом. Более того, идентификатор URI для этого пространства имен генерируется автоматически, поэтому его невозможно узнать и использовать за пределами данного класса. Наконец, защищаемый класс определяет сами переменные и методы с ограниченным доступом.
Для обращения к методу или переменной с ограниченным доступом (например, secretData или secretMethod ( ) ) потенциальный класс должен получить общеизвестные «ключи от парадной двери». Другими словами, он должен получить ссылку на пространство имен, которое уточняет методы и переменные с ограниченным доступом. Однако защищаемый класс предоставит эту ссылку только в том случае, если потенциальный класс — будем называть его «вызывающим классом» — является одним из элементов массива authorizedClasses.
В нашем примере вызывающий класс будет просить у класса ShelteredClass ссылку на пространство имен restricted, используя метод getRestricted Namespace ( ) класса ShelteredClass. Метод getRestrictedAccess ( ) принимает экземпляр вызывающего класса в качестве аргумента. Если экземпляр вызывающего класса оказывается авторизованным, метод getRestrictedNames расе ( ) вернет ссылку на пространство имен restricted. В противном случае метод вернет значение null, которое сообщает о том, что вызывающий класс не имеет права обращаться к методам и переменным с ограниченным доступом. Рассмотрим код метода getRestrictedNamespace ( ):
public function getRestrictedNamespace
(cal lerObject:Object.):Namespace { // Проверяем, есть ли объект cal1erObject в массиве authorizedClasses. for each (var authorizedClass:Class in authorizedClasses) { // Если вызывающий объект является экземпляром авторизованного класса… if (callerObject is authorizedClass) { // …возвращаем обратно ссылку на пространство имен restricted // («ключи от парадной двери») return restricted;
// Вызывающий объект не является экземпляром
// авторизованного класса, поэтому
// запрещаем дальнейшее обращение к переменной
// и методу с ограниченным доступом.
Руководство по actionscript. часть 3, стр. 017
Return null;
}
В листинге 17.5 продемонстрирован весь код класса ShelteredClass, включая метод getRestrictedNamespace ( ).
Листинг 17.5. Класс ShelteredClass
package { // Это защищаемый класс public class ShelteredClass {
// Пространство имен restricted уточняет переменные
// и методы, доступ к которым ограничен.
private namespace restricted;
// Это массив авторизованных классов. В данном примере // определен только один авторизованный класс: Caller, private var authorizedClasses:Array = [ Caller ];
// Это переменная с ограниченным доступом.
// К ней могут обращаться только авторизованные классы.
restricted var secretData.-String = «No peeking»;
// Это метод с ограниченным доступом. // К нему могут обращаться только авторизованные классы, restricted function secretMethod ( ):void { trace(«Restricted method secretMethod( ) called»);
}
public function getRestrictedNamespace
(callerObject:Object):Namespace { // Проверяем, есть ли объект callerObject в массиве authorizedClasses. for each (var authorizedClass:Class in authorizedClasses) { // Если вызывающий объект является экземпляром // авторизованного класса… if (cal1erObject is authorizedClass) { // …возвращаем обратно ссылку на пространство имен restricted // («ключи от парадной двери») return restricted;
}
}
// Вызывающий объект не является экземпляром // авторизованного класса, поэтому // запрещаем дальнейшее обращение к переменной // и методу с ограниченным доступом, return null;
Теперь рассмотрим класс Caller — класс, который желает получить доступ к методам и переменным с ограниченным доступом класса Shel teredClas s. Принимая во внимание значения элементов массива authorizedClasses класса ShelteredClass, мы знаем, что класс Caller является допустимым. В нашем примере Caller также является основным классом приложения, поэтому он расширяет класс Sprite. Класс Caller создает экземпляр класса ShelteredClass в своем методе конструктора и присваивает этот экземпляр переменной shelteredOb j ect.
Руководство по actionscript. часть 3, стр. 018
Package { import flash. display.*;
public class Caller extends Sprite { private var shelteredObject:ShelteredClass;
public function Caller ( ) { shelteredObject = new ShelteredClass( );
}
}
}
Чтобы вызвать метод secretMethod ( ) класса ShelteredClass, объект Caller должен сначала получить ссылку на пространство имен restricted. Для этого объект Caller передает себя в метод getRestrictedNamespace ( ) и присваивает результат (либо пространство имен restricted, либо значение null) переменной key для дальнейшего использования.
var key:Namespace = shelteredObject. getRestrictedNamespace(this);
Далее, перед тем как вызвать метод secretMethod ( ), объект Caller проверяет, ссылается ли переменная key на допустимое пространство имен. Если это так, объект Caller использует переменную key в качестве пространства имен для вызова метода secureMethod ( ):
if (key!= null) { shelteredObject. key::secureMethod( );
}
Для удобства метод с именем callSecretMethod ( ) нашего класса Caller включает код, который вызывает метод secretMethod ( ):
public function callSecretMethod ( ):void { var key:Namespace = shelteredObject. getRestrictedNamespace(this); if (key!= null) { shelteredObject. key:: secretMethod( );
}
}
Листинг 17.6 демонстрирует весь код рассматриваемого класса Caller, включая метод callSecretMethod ( ) и другой удобный метод displaySecret ( ), который обращается к переменной secretData с ограниченным доступом, используя тот же основной принцип.
Руководство по actionscript. часть 3, стр. 019
Листинг 17.6. Класс Caller
package { import flash. display.*;
public class Caller extends Sprite {
private var shelteredObject:ShelteredClass;
public function Caller ( ) { shelteredObject = new ShelteredClass( ); callSecretMethod( ); displaySecret( );
}
public function callSecretMethod ( ):void { var key-.Namespace = shelteredObject. getRestrictedNamespace(this) ; if (key!= null) { shelteredObject. key::secretMethod( );
}
}
public function displaySecret ( ):void { var key:Namespace = shelteredObject. getRestrictedNamespace(this); if (key!= null) { trace(shelteredObject. key. :secretData);
}
}
}
}
Пример: реализация режимов работы программы
Последним рассматриваемым примером будет электронный словарь, который позволяет переводить с японского языка на английский и наоборот. Словарь демонстрирует использование режимов программы — область программирования на языке ActionScript, где применяются пространства имен, с самым большим потенциалом. Находясь в «режиме японского языка», словарь возвращает английский перевод для японских слов; находясь в «режиме английского языка», словарь возвращает японский перевод для английских слов. Каждый режим представляется пространством имен: j apanese для японско-английского режима и english для англо-японского режима.
Руководство по actionscript. часть 3, стр. 020
В этом примере задействованы следующие участники:
? Japane se — пространство имен для переменных и методов, относящихся к японскому языку;
? English — пространство имен для переменных и методов, относящихся к английскому языку;
? QueryManager — класс, осуществляющий поиск слов;
? SearchOptions — этот класс содержит базовые настройки для операции поиска;
? JapaneseSearchOptions — класс, содержащий настройки, характерные для операции поиска на японском языке;
? Engl ishSearchOpt ions — данный класс хранит настройки, характерные для операции поиска на английском языке;
? JEDictionary — основной класс приложения.
Рассмотрим всех перечисленных участников по отдельности, принимая во внимание, что данный пример не является полнофункциональным, и в тех местах, где должен осуществляться реальный поиск по базе данных, в нем используется код-заполнитель.
Начнем с рассмотрения определений пространств имен japanesenenglish, чей код уже должен быть вам знаком:
package {
public namespace english = «http://www. example. com/jedict/english»;
}
package {
public namespace japanese = «http://www. example. com/jedict/japanese»;
}
Далее идет класс QueryManager, который определяет два метода для поиска слова, — japanese: : search ( ) и english: : search ( ). Вызов подходящего метода происходит в зависимости от текущего режима программы. Каждый метод search ( ) принимает apryMeHT’options, который определяет настройки поиска в виде либо объекта класса JapaneseSearchOptions, либо объекта класса EnglishSearchOpt ions соответственно. Далее при рассмотрении класса JEDictionary мы увидим, что настройки поиска выбираются в соответствии с текущим режимом программы. Вот код класса QueryManager:
package { public class QueryManager {
japanese function search (word:String.
Руководство по actionscript. часть 3, стр. 021
Options.-JapaneseSearchOptions): Array { trace («Now searching for ‘» + word + ‘»An»
+ » Match type: » + options. getMatchType( ) + «\n»
+ » English language variant: » + options. getEnglishVariant( ));
// Расположенный здесь код (не показан) должен выполнять поиск // в японско-английском словаре и возвращать результаты. * // но для эксперимента мы будем просто возвращать предопределенный // список результатов:
return ["English Word 1". "English Word 2". "etc"];
}
english function search (word.-String.
options:Engl ishSearchOptions):Array { traceCNow searching for ‘» + word + ‘»An»
+ » Match type: » + options. getMatchType( ) + «\n»
+ » Use kanji in results: » + options. getKanjiInResults( ));
// Расположенный здесь код (не показан) должен выполнять поиск // в англо-японском словаре и возвращать результаты,
// но для эксперимента мы будем просто возвращать предопределенный // список результатов;
return ["Japanese Word 1", "Japanese Word 2". "etc"];
}
}
}
Теперь рассмотрим три класса, предоставляющие настройки поиска: SearchOptions и два его подкласса JapaneseSearchOptions и EnglishSearchOptions. Класс SearchOptions задает, с использованием какого режима программа должна выполнять поиск указанной строки: «точного совпадения» (искомое слово должно полностью совпадать со строкой поиска), «совпадает начало» (все искомые слова должны начинаться со строки поиска) или «содержит совпадение» (все искомые слова должны включать строку поиска).