Руководство по actionscript. часть 2, стр. 077

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

Обработка нескольких типов исключений

Пример исключения из предыдущего раздела был чрезмерно упрощен. Что произойдет, если наш метод генерирует ошибки нескольких типов? Все ошибки будут отправлены в один и тот же блок catch? Что ж, это зависит от разработчика. Конечно, они все могут быть отправлены в один блок catch, однако чаще всего обработка различных типов ошибок выполняется отдельными блоками cat ch — и это является хорошей практикой. Рассмотрим почему.

Предположим, что мы хотим получить набор более детальных сообщений об ошибках в нашем методе setName ( ): одно сообщение для недопустимых данных, одно — для слишком короткого имени, еще одно — для слишком длинного имени.

Тело нашего модифицированного метода setName ( ) могло бы выглядеть следующим образом:

if (newNameлndexOfC «) == 0) {

// Имена не могут начинаться с пробела…

throw new Error(«Invalid pet name specified.»); } else if (newName == «») {

throw new Error(«Pet name too short.»); } else if (newName. length > Virtual Pet. maxNameLength) {

throw new ErrorCPet name too long.»);

}

Чтобы обработать все три возможные сообщения об ошибках, генерируемые в нашем новом методе setName ( ), мы могли бы записать код нашей инструкции try/catch /finally следующим образом:

try {

somePet. setName(«некоеИмяЖивотного»);

// Если мы находимся здесь, значит, исключение не возникло;, продолжаем // выполнение, как планировалось ранее. traceCPet name set successfully.»); } catch (e:Error) { switch (e. message) {

case «Invalid pet name specified.»:

traceC’An error occurred: » + e. message);

traceCPlease specify a valid name.»);

break;

case «Pet name too short.»:

traceC’An error occurred: » + e. message);

traceCPlease specify a longer name.»);

break;

case «Pet name too long.»:

traceC’An error occurred: » + e. message);

traceCPlease specify a shorter name.»);

break;

}

}

Надо признаться, что этот код работает, однако он имеет множество недостатков. Самый первый и наиболее серьезный недостаток состоит в том, что ошибки, отличаются друг от друга только текстом в строке, которая скрыта внутри класса Vi rtual Pet. Всякий раз, когда мы хотим узнать, какие типы ошибок могут возникать в методе setName ( ), мы вынуждены обращаться к коду класса VirtualPet и искать строки сообщений об ошибках. Использование сообщений для идентификации ошибок между различными методами и классами зачастую приводит к появлению ошибок, вызванных человеческим фактором, и затрудняет поддержку нашего кода. Второй недостаток заключается в том, что оператор switch сам по себе сложен для чтения. Это ненамного лучше использования, скажем, числовых кодов ошибок вместо формальных исключений.

Руководство по actionscript. часть 2, стр. 078

К счастью, существует официальный (и элегантный) способ обработки нескольких типов исключений. Каждый блок try может иметь любое количество вспомогательных блоков catch. Когда исключение генерируется в блоке try, который имеет несколько блоков catch, среда Flash выполняет тот блок catch, тип данных параметра которого совпадает с типом данных значения сгенерированного исключения.

Рассмотрим общий синтаксис оператора try с несколькими блоками catch: try {

// Код, который может генерировать исключения. } catch (е:Тип0шибки1) {

II Код обработки ошибки с типом Тип0шибки1. } catch (е:Тип0шибки2) {

II Код обработки ошибки с типом Тип0шибки2. } catch (е:ТипОшибкип) {

II Код обработки ошибки с типом ТипОшибкип.

)

Если бы оператор throw в предыдущем блоке try сгенерировал исключение, применив в качестве параметра выражение типа Тип0шибки1, был бы выполнен первый блок catch. Например, следующий код приведет к выполнению первого блока catch:

throw new ТипОшибкиК );

Если бы в оператор throw было передано выражение типа Тип0шибки2, был бы выполнен второй блок catch и т. д. Как уже известно, в языке ActionScript выражение оператора throw может принадлежать любому типу данных. Однако помните, что в большинстве программ исключения представляются только экземплярами класса Error или одного из его подклассов и это является хорошей практикой.

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

Определение степени детализации типов исключений. Следует ли определять подкласс класса Error для каждой исключительной ситуации? Обычно ответ на этот вопрос отрицателен — вам не потребуется такая степень детализации, поскольку во многих случаях несколько исключительных ситуаций могут иметь одинаковый смысл. Если вам не нужно задавать различие между несколькими исключительными ситуациями, то можете объединить эти ситуации в одном пользовательском подклассе класса Error. Например, вы можете определить один подкласс класса Error с именем Inval idlnputExcept ion для решения широкого круга проблем, связанных с вводом данных.

С другой стороны, вы должны определять отдельный подкласс класса Error для каждой исключительной ситуации, которая, по вашему мнению, отличается от других возможных ситуаций. Чтобы разобраться, когда следует создавать новый подкласс для конкретной исключительной ситуации, а также продемонстрировать возможность группирования нескольких ситуаций в одном подклассе, вернемся к методу setName ( ).

Руководство по actionscript. часть 2, стр. 079

Ранее мы генерировали три исключения в методе setName ( ). Все три исключения использовали базовый класс Error. Приведем этот код снова:

if (newName. indexOfC «) == 0) {

// Имена не могут начинаться с пробела…

throw new ЕггогС»Invalid pet name specified.»); } else if (newName == «») {

throw new ErrorCPet name too short.»); } else if (newName. length > VirtualPet. maxNameLength) {

throw new ErrorCPet name too long.»);

}

В данном коде, чтобы провести различие между исключениями класса Virtual Pet и остальными исключениями в нашем приложении, мы использовали переменную message класса Error, которая, как уже известно, делает наши исключения неудобными для использования и может привести к появлению ошибок, вызванных человеческим фактором. Для отличия ошибок, относящихся к классу Virtual Pet, от других ошибок в нашем приложении лучше определить пользовательский подкласс класса Error с именем VirtualPetNameException, как показано в следующем коде:

// Код в файле VirtualPetNameException. as: package zoo {

public class VirtualPetNameException extends Error { public function VirtualPetNameException ( ) { // Передаем сообщение об ошибке в конструктор класса Error, которое // будет присвоено переменной message данного объекта ‘ superCInvalid pet name specified.»);

}

}

}

Теперь, когда у нас появился класс VirtualPetNameException, метод setName ( ) может генерировать свой собственный тип ошибки, как показано в следующем коде:

public function setName (newName:String):void { if (newName. indexOfС «) == 0) {

throw new VirtualPetNameException( ); } else if (newName == «») {

throw new VirtualPetNameException( ); } else if (newName. length > Virtual Pet. maxNameLength) {

throw new VirtualPetNameException( );

}

petName = newName;

}

Обратите внимание, что в предыдущем описании метода для всех трех исключительных ситуаций, относящихся к классу Virtual Ре t, генерируется один и тот же тип ошибки (VirtualPetNameException). Как разработчики класса VirtualPet мы столкнулись с проблемой определения степени детализации исключительных ситуаций. Мы должны решить не только то, в какой мере сообщения об ошибках класса

VirtualPet будут отличаться от других ошибок приложения, но и то, насколько эти ошибки будут отличаться друг от друга. У нас есть следующие варианты:

Вариант 1. Использовать один класс для исключительных ситуаций класса VirtualPet

В этом случае мы оставляем предыдущее описание метода s е tName ( ) как есть. Как вскоре станет известно, этот вариант позволяет отличать ошибки класса Vi rtual Ре t от других базовых ошибок в программе, однако мы не сможем отличить между собой три внутренние разновидности ошибок класса Virtual Pet (недопустимые данные, слишком короткое имя животного и слишком длинное имя животного).

Руководство по actionscript. часть 2, стр. 080

Вариант 2. Упростить код, но по-прежнему использовать один класс для исключительных ситуаций класса VirtualPet

В данном случае мы изменяем метод setName ( ) таким образом, чтобы проверка всех трех исключительных ситуаций выполнялась в одном операторе if. Этот вариант аналогичен предыдущему, но использует более сжатый код.

Вариант 3. Использовать отладочные сообщения для нахождения различий между ошибками.

В данном случае мы добавляем конфигурируемые отладочные сообщения в класс VirtualPetNameException. Этот вариант незначительно увеличивает степень детализации по сравнению с двумя предыдущими, но только для удобства разработчика и только на этапе отладки.

Вариант 4. Создать пользовательский класс исключения для каждой исключительной ситуации.

Используя это вариант, мы создаем два пользовательских подкласса класса VirtualPetNameException: VirtualРеtlnsufficientDataException и VirtualPetExcessDataException. Этот вариант обеспечивает наибольшую степень детализации. Он позволяет программе независимо реагировать на три разновидности ошибок, относящихся к классу VirtualPet, используя формальную логику ветвлений.

Рассмотрим каждый из описанных вариантов.

Варианты 1 и 2. Использование одного пользовательского типа исключения. Первый вариант состоит в применении предыдущего описания метода setName ( ), которое генерирует ошибку одного и того же типа (VirtualPetNameException) для всех трех исключительных ситуаций, относящихся к классу Virtual Pet. Поскольку для генерации исключений этот метод использует класс VirtualPetNameException, а не класс Error, исключения класса Vi rtual Pet уже отличаются от других базовых исключений. Пользователи метода setName ( ) могут применять код, аналогичный следующему, для отличия ошибок, относящихся к классу VirtualPet, от других базовых ошибок:

try {

// Этот вызов метода setName( ) приведет к возникновению исключения // VirtualPetNameException. somePet. setName(«»);

// Другие инструкции в этом блоке try могут генерировать // другие базовые ошибки. Для демонстрационных целей

// мы непосредственно сгенерируем // базовую ошибку.

throw new Error(«A generic error.»); } catch (e:VirtualPetNameException) { // Здесь обрабатываются ошибки, связанные с именем объекта класса // VirtualPet.

Руководство по actionscript. часть 2, стр. 081

TraceC’An error occurred: » + e. message); traceCPlease specify a valid name.»); } catch (e:Error) { // Здесь обрабатываются все остальные ошибки. traceC’An error occurred: » + e. message);

}

Для большого количества приложений степень детализации, обеспечиваемая классом Virtual РеtNameExcept ion, оказывается достаточной. В этом случае мы должны по крайней мере переписать метод setName ( ), чтобы он не содержал избыточный код (трижды генерирующий исключение VirtualPetNameException). Рассмотрим переписанный код (представляющий вариант 2 из предыдущего списка):

public function setName (newName:String):void { if (newName. indexOfC «) == 0 11 newName == «»

jj newName. length > Virtual Pet. maxNameLength) { throw new VirtualPetNameException( );

}

petName = newName;

r*^! 1 Переписывание кода с целью улучшения его структуры без изменения существующего

-ttf

поведения называется рефакторингом.

Вариант 3. Применение конфигурируемых отладочных сообщений. Вариант 3 заключается в добавлении конфигурируемых отладочных сообщений в класс VirtualPetNameException. Варианты 1 и 2 позволяют отличить исключение класса VirtualPet от других исключений в приложении, но не позволяют отличить исключение «слишком длинное» от исключения «слишком короткое». Если вы чувствуете, что отладка проблемы, связанной с именем объекта класса Virtual Pet, затруднена отсутствием знания о том, является имя объекта класса VirtualPet слишком длинным или слишком коротким, можно изменить класс VirtualPetNameException таким образом, чтобы он принимал дополнительное описание (наподобие общеизвестного крестика на память). Рассмотрим измененный код класса VirtualPetNameException:

package zoo {

public class VirtualPetNameException extends Error { // Предоставляет конструктор, который позволяет указывать // пользовательское сообщение. Если пользовательское сообщение не // указано, используется стандартное сообщение об ошибке public function VirtualPetNameException (

message:String = «Invalid pet name specified.») {

super(message);

}

}

}

Чтобы воспользоваться модифицированным классом Virtual PetNameExcept ion в методе setName ( ), вернемся к коду метода setName ( ), использованному в варианте 1, и добавим отладочные сообщения об ошибке, как показано в следующем коде:

public function setName (newName:String):void { if (newName. indexOfC «) == 0) { // В данном случае отлично подойдет стандартное сообщение об ошибке, // поэтому не стоит утруждать себя указанием пользовательского сообщения // об ошибке.

Руководство по actionscript. часть 2, стр. 082

Throw new VirtualPetNameException( ): } else if (newName == «») {

// Вот пользовательское сообщение об ошибке «слишком короткое».

throw new VirtualPetNameException(«Pet name too short.»); } else if (newName. length > Virtual Pet. maxNameLength) {

// Вот пользовательское сообщение об ошибке «слишком длинное».

throw new Virtual PetNameException(«Pet name too long.»);

}

petName = newName;

}

Теперь, когда метод setName ( ) задает пользовательские сообщения об ошибках, упрощается отладка проблем, связанных с именем объекта класса VirtualPet, поскольку у нас появляется возможность получить больше информации о возникшей ошибке. Использование метода setName ( ) не изменилось, но теперь, если что-то пойдет не так, мы будем лучше проинформированы, как показано в следующем коде:

try {

// Этот вызов метода setName( ) приведет к возникновению исключения

// VirtualPetNameException.

somePet. setName(«»); } catch (e:VirtualPetNameException) {

// Здесь обрабатываются ошибки, связанные с именем объекта класса

// VirtualPet. В данном случае полезным отладочным сообщением является:

// An error occurred: Pet name too short

// (Возникла ошибка: Имя животного слишком короткое).

Руководство по actionscript. часть 2, стр. 083

TraceC’An error occurred: » + е. message); } catch (e:Error) {

// Здесь обрабатываются все остальные ошибки.

traceC’An error occurred: » + е. message);

}

Вариант 4. Пользовательские подклассы класса VirtualPetNameException.

В варианте 3 мы добавляли конфигурируемые отладочные сообщения в класс Virtual PetNameExcept ion. Он помог выявить проблему в нашем коде на этапе разработки, однако этот вариант не позволяет программе выполнить независимые действия по восстановлению работоспособности после возникновения отдельных

ошибок класса VirtualPet. Чтобы программа могла выполнить отдельные ветки кода в зависимости от типа сгенерированной ошибки класса VirtualPet, нам потребуются пользовательские подклассы класса VirtualPetNameException, описанные в варианте 4.

I Если вы хотите, чтобы программа могла находить различия между исключительными м$ j * ситуациями, определяйте отдельный подкласс класса Error для каждой ошибки. Не по-

_ ц# лагайтесь исключительно на значение переменной message, чтобы реализовать логику

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

Чтобы иметь возможность устанавливать различие между тремя исключительными ситуациями класса VirtualPet, создадим три подкласса класса Error: VirtualPetNameException, Virtual Pet Insufficient DataExcept ion и Vir tualPetExcessDataException. Первый класс непосредственно расширяет класс Error. Оба следующих класса расширяют класс VirtualPetNameException, поскольку мы хотим отличать эти специфические типы ошибок от базового исключения, обозначающего недопустимые данные.

Рассмотрим исходный код наших трех подклассов класса Error, представляющих ошибки класса VirtualPet:

// Код в файле VirtualPetNameException. as:

package zoo {

public class VirtualPetNameException extends Error { public function VirtualPetNameException (

message:String = «Invalid pet name specified.») {

super(message);

}

}

}

// Код в файле Virtual PetInsufficientDataException. as:

package zoo { public class VirtualPetlnsufficientDataException

extends VirtualPetNameException { public function VirtualPetlnsufficientDataException ( ) { super(«Pet name too short.»);

}

}

}

// Код в файле VirtualPetExcessDataException. as:

package zoo { public class VirtualPetExcessDataException

extends VirtualPetNameException { public function VirtualPetExcessDataException ( ) { super(«Pet name too long.»);

Каждый класс определяет значение своей переменной message и не позволяет изменять его в процессе использования. При обработке любого из описанных исключений класса VirtualPet наша программа будет руководствоваться типом данных исключения (а не значением переменной message) для нахождения различия между тремя типами исключений.

Руководство по actionscript. часть 2, стр. 084

Теперь, когда у нас появилось три типа исключений, добавим их генерацию в наш метод setName ( ):

public function setName (newName:String):void { if (newName. indexOfC «) == 0) {

throw new VirtualPetNameException( ); } else if (newName == «») {

throw new VirtualPetInsufficientDataException( ); } else if (newName. length > Virtual Pet. maxNameLength) {

throw new VirtualPetExcessDataException( );

}

petName = newName;

}

Обратите внимание, что в конструкторы различных исключений класса Virtual Pet не передаются никакие описания ошибок. Еще раз повторим, что описание каждого исключения задается в соответствующем пользовательском подклассе класса Error через его переменную message.

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

Рассмотрим, как добавить в код логику ветвления, основанную на типах исключений, которые могут быть сгенерированы методом setName ( ). Особое внимание обратите на типы данных параметров и размещение каждого блока catch.

try {

b. setNameCнекоеИмяЖивотного»); } catch (e:VirtualPetExcessDataException) {

// Обработка ситуации «слишком длинное».

Руководство по actionscript. часть 2, стр. 085

TraceC’An error occurred: » + e. message);

traceCPlease specify a shorter name.»); } catch (e:VirtualPetlnsufficientDataException) {

// Обработка ситуации «слишком короткое».

traceC’An error occurred: » + e. message);

traceCPlease specify a longer name.»); } catch (e:VirtualPetNameException) {

// Обработка общих ошибок, связанных с именем.

traceC’An error occurred: » + е. message);

traceCPlease specify a valid name.»);

В приведенном коде, если метод setName ( ) сгенерирует исключение Virtu alPetExcessDataException, будет выполнен первый блок catch. Если метод сгенерирует исключение VirtualPetlnsufficientDataException, будет выполнен второй блок catch. И наконец, если метод сгенерирует исключение VirtualPetNameException, будет выполнен третий блок catch. Обратите внимание, что в блоках catch сначала перечислены специфические типы данных ошибок, а затем — общие. При возникновении исключения выполняется тот блок catch, у которого первым совпадет тип данных параметра с типом данных исключения.

Таким образом, если мы изменим тип данных параметра первого блока catch на тип VirtualPetNameException, первый блок catch будет выполняться для всех трех типов исключений!

Вспомните, что класс VirtualPetNameException является суперклассом для обоих классов VirtualPetlnsufflcientDataException и VirtualPetExcessDataException, поэтому считается, что они соответствуют типу данных VirtualPetNameException.

Фактически мы могли бы предотвратить выполнение всех блоков catch, разместив первым новый блок catch, типом данных параметра которого является Error:

try {

b. setName(«некоеИмяЖивотного»); } catch (e:Error) {

// Обрабатываем все ошибки. Никакие другие блоки catch

// выполняться не будут.

Руководство по actionscript. часть 2, стр. 086

TraceC’An error occurred:» + е. message);

traceC’The first catch block handled the error.»); } catch (e:VirtualPetExcessDataException) {

// Обработка ситуации «слишком длинное».

traceC’An error occurred: » + e. message);

traceCPlease specify a shorter name.»); } catch (e:\ZirtualPetInsufficientDataException) {

// Обработка ситуации «слишком короткое».

traceC’An error occurred: » + e. message);

traceCPlease specify a longer name.»); } catch (e:VirtualPetNameException) {

// Обработка общих ошибок, связанных с именем.

traceC’An error occurred: » + е. message);

traceCPlease specify a valid name.»);

}

Очевидно, что попытка добавить первый блок catch в предыдущем коде обречена на провал, но этот пример иллюстрирует иерархическую природу обработки ошибок. Поместив базовый блок catch в самое начало списка обработчиков, мы можем обработать все ошибки в одном блоке. И наоборот, если поместить базовый блок catch в конец списка, мы можем создать «страховочную сетку», которая будет обрабатывать любые ошибки, не «пойманные» предыдущими блоками catch. Например, в следующем коде последний блок catch будет выполнен только в том

случае, если блок try сгенерирует исключение, которое не принадлежит типам данных VirtualPetExcessDataException, Virtual Pet In sufficient Data Exception или VirtualPetNameException:

try {

b. setName(«некоеИмяЖивотного»); } catch (e:VirtualPetExcessDataException) {

// Обработка переполнения.



Полезные ссылки
Случайные записи
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 020
  • 17.03.2011">Руководство по actionscript. часть 2, стр. 153
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 004
  • 02.03.2011">Руководство по actionscript. часть 5, стр. 106
  • 05.03.2011">Руководство по actionscript. часть 4, стр. 155
  • 06.07.2011">Новая серия ультрапортативных ноутбуков Acer
  • 17.06.2010">Самоучитель по креативному веб-дизайну. Книга 4, стр.9
  • 09.03.2011">Руководство по actionscript. часть 4, стр. 055
  • 13.07.2012">В сеть утекло 450 тысяч паролей Yahoo! Voices
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 068
  • 17.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.115
  • 18.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.83
  • 10.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.120
  • 04.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.8
  • 16.03.2011">Руководство по actionscript. часть 3, стр. 009
Опрос

Какие цвета вы предпочитаете?

View Results

Loading ... Loading ...