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

Var г = new RectangleC ); r. setSize(4,5):

trace(r. getArea( )); // Выводит: 20

В отличие от этого, в следующем коде мы вызываем метод setSize ( ) через экземпляр класса Square. На этот раз среда выполнения Flash знает, что классом экземпляра является Square, поэтому версия метода setSize ( ) вызывается именно из этого класса, а не из класса Rectangle:

var s = new Square( ): s. setSize(4,5):

trace (s. getArea( )); // Выводит: 0 (Метод setSize( ) предотвращает

// присваивание недопустимых значений)

В предыдущем коде результат вызова метода s. get Area ( ) равен 0. Это говорит о том, что значения переменных w и h не были установлены при вызове метода s. setSize ( ). В версии метода setSize ( ) класса Square значения переменным w и h присваиваются только в том случае, когда значения параметров newW и newH равны между собой.

Вызов перекрытого метода экземпляра. Когда подкласс перекрывает метод экземпляра, версия этого метода, определенная в суперклассе, не теряется. Экземпляры подкласса могут обращаться к этой версии метода посредством оператора super, позволяющего вызывать перекрытый метод следующим образом:

super. имяМетода (аргумент!, аргумент2… аргумент);

В этом коде имяМетода обозначает имя вызываемого перекрытого метода, а аргумент!, аргумент2. . . аргумент — список аргументов, передаваемых в этот метод (другие варианты использования оператора super будут рассмотрены далее в этой главе).

В качестве примера вызова перекрытого метода вернемся к сценарию с классами Square и Rectangle. В предыдущем разделе наш метод Square. setSize ( ) без необходимости дублировал код метода Rectangle. setSize ( ). Рассмотрим версию метода, определенную в классе Rectangle:

public function setSize (newW, newH) { w = newW; h = newH;

}

В версии метода setSize ( ), определенной в классе Square, просто добавлен оператор if:

override public function setSize (newW, newH) { if (newW == newH) { w = newW; h = newH;

}

}

Чтобы избежать дублирования кода, используемого в обоих методах для присваивания значений переменным w и h, мы можем воспользоваться оператором super, как показано в следующей модифицированной версии метода Square. setSize ( ):

override public function setSize (newW, newH) { if (newW == newH) { // Вызов метода setSize( ) суперкласса над текущим экземпляром super. setSize(newW, newH);

}

}

Модифицированный метод setSize ( ) KnaccaSquare проверяет, одинаковы ли значения параметров newW и newH. Если значения одинаковы, то вызывается метод setSize ( ) класса Rectangle над текущим экземпляром. Метод setSize ( ) класса Rectangle позаботится о присваивании значений переменным w и п.

Приведенный пример с методом setSize ( ) демонстрирует, как подкласс может перекрывать метод для ограничения его поведения. Кроме того, подкласс может перекрывать метод для дополнения его поведения. Например, следующий код создает класс ScreenRectangle, являющийся подклассом класса Rectangle и отображающий на экране прямоугольник. Подкласс ScreenRectangle перекрывает метод s е t S i z е ( ), сохраняя поведение перекрываемого метода, но при этом добавляя вызов метода draw ( ), в результате чего размеры прямоугольника на экране изменяются всякий раз при вызове метода s е t S i z е ( ):

public class ScreenRectangle extends Rectangle { override public function setSize (newW, newH) { // Вызов версии метода setSize( ) класса Rectangle super. setSize(newW, newH);

// Теперь отображаем прямоугольник на экране draw( );

}

public function draw ( ) { // Здесь размещается код, отображающий прямоугольник на экране

Перекрытие можно использовать и для аннулирования поведения метода. Методика очень проста: версия перекрытого метода подкласса не выполняет никаких действий. Например, следующий код демонстрирует подкласс ReadOnlyRectangle, блокирующий метод setSize ( ) класса Re с tangle, в результате чего исключается возможность изменения размера для экземпляра этого подкласса:

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

Public class ReadOnlyRectangle extends Rectangle { // Следующее определение фактически блокирует метод setSizeC ) // для экземляров класса ReadOnlyRectangle. override public function setSize (newW, newH) { // Никаких действий

}

}

Методы-конструкторы в подклассах

Теперь, когда мы рассмотрели поведение методов и переменных экземпляра относительно наследования, обратим наше внимание на методы-конструкторы.

Вспомним, что метод-конструктор инициализирует экземпляры класса следующими способами:

? вызывая методы, которые выполняют задачи настройки;

? присваивая значения переменным созданного объекта.

Когда происходит расширение класса, подкласс может определять собственный метод-конструктор. На конструктор подкласса возлагаются следующие функции:

? выполнять задачи настройки, относящиеся к подклассу;

? присваивать значения переменным, описанным в подклассе;

? вызывать метод-конструктор подкласса (иногда называемый суперконструктором).

Если в подклассе определен метод-конструктор, в нем обязательно должен вызываться конструктор суперкласса с помощью ключевого слова super. Более того, конструктор суперкласса должен вызываться до обращения к любой переменной или методу экземпляра. Если конструктор суперкласса не будет вызван явно, компилятор автоматически добавит вызов конструктора суперкласса без аргументов. И наконец, ключевое слово super не должно использоваться в методе-конструкторе более одного раза.

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

? исключается вызов методов над объектом, который еще не был проинициали-зирован;

? устраняется доступ к переменным объекта, который еще не был проинициали-зирован;

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

Не путайте две разновидности оператора super. Первая разновидность — super() — вызывает метод-конструктор суперкласса. Вторая разновидность — super. имяМетода() — вызывает метод суперкласса. Использование первой разновидности допустимо только в методе-конструкторе. Вторая разновидность может многократно применяться в любом месте метода-конструктора или метода экземпляра.

Рассмотрим применение оператора super для вызова метода-конструктора суперкласса в простейшем случае. Следующий код описывает класс А с пустым методом-конструктором:

public class А { public function А ( ) { }

}

Следующий код описывает класс В, который расширяет класс А. Внутри метода-конструктора класса В мы используем оператор super для вызова метода-конструктора класса А:

public class В extends А { // Конструктор подкласса public function В ( ) {

// Вызов метода-конструктора суперкласса

super( );

}

}

С точки зрения функциональности следующие описания двух методов-конструкторов являются синонимами. В первом случае метод-конструктор суперкласса вызывается явно; во втором случае среда выполнения Flash вызывает метод-конструктор суперкласса неявно.

public function В ( ) { // Явный вызов метода-конструктора суперкласса super( );

}

public function В ( ) { // Вызов конструктора отсутствует. // Среда Flash вызовет конструктор автоматически

}

Если в подклассе метод-конструктор не определен вообще, то компилятор языка ActionScript автоматически создаст метод-конструктор и добавит в него одну инструкцию — вызов оператора super. Следующие два описания класса В функционально являются идентичными. Первое описание в явном виде представляет то, что для второго описания автоматически создает компилятор:

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

// Определение конструктора в явном виде public class В extends А {

// Явное объявление конструктора

public function В ( ) { // Явный вызов метода-конструктора суперкласса

super( );

}

}

// Позволить компилятору автоматически создать конструктор по умолчанию

public class В extends А {

}

Метод-конструктор подкласса может (а зачастую так и происходит) иметь другие параметры, нежели его коллега из суперкласса. Например, в нашем классе Rectangle можно было бы определить конструктор с параметрами width и height, а класс Square мог бы иметь собственный конструктор с одним параметром side (у квадратов ширина и высота совпадает, поэтому не нужно указывать оба значения). В листинге 6.1 продемонстрирован этот код.

Листинг 6.1. Конструкторы классов Rectangle и Square

public class Rectangle { protected var w = 0; protected var h = 0;

// Конструктор класса Rectangle public function Rectangle (width, height) { setSize(width, height);

}

public function setSize (newW, newH) { w = newW; h = newH;

}

public function getArea ( ) { return w * h;

}

}

public class Square extends Rectangle { // Конструктор класса Square public function Square (side) {

// Передаем параметр side в конструктор класса Rectangle

super(side. side);

}

override public function setSize (newW, newH) { if (newW == newH) { // Вызов метода setSize( ) суперкласса над текущим экземпляром super. setSize(newW, newH);

}

}

}

Кстати, вы могли бы задаться вопросом, а не лучше ли определить метод s е t S i z е ( ) класса Square с одним параметром s ide, чем иметь два отдельных параметра width

и height. Это демонстрирует следующая версия метода setSize ( ) (обратите внимание, что в методе больше не нужна проверка значений параметров newW и newH на равенство).

override public function setSize (side) { // Вызов метода setSize( ) суперкласса над текущим экземпляром super. setSize(side, side);

}

Хотя приведенная версия метода setSize ( ), несомненно, является более подходящей для класса Square, она приведет к ошибке, поскольку имеет меньшее количество параметров, чем версия метода setSize ( ) класса Rectangle (помните, что количество параметров, определенных в перекрывающем методе, должно совпадать с количеством параметров перекрываемого метода). Позднее, в подразд. «Наследование в сравнении с композицией» разд. «Теория наследования», мы рассмотрим альтернативный допустимый вариант реализации версии метода setSize( )с одним параметром в классе Square.

При определении метода-конструктора подкласса обязательно указывайте все требуемые аргументы для конструктора суперкласса. Метод-конструктор класса ColoredBall из следующего примера определен неправильно, поскольку в метод-конструктор суперкласса не передается необходимая информация:

public class Ball { private var r;

public function Ball (radius) { r = radius;

}

}

public class ColoredBall extends Ball { private var c;

// Это проблематичный конструктор… public function ColoredBall (color) {

// Ой! Отсутствует вызов оператора super( ). Здесь произойдет ошибка,

// поскольку в конструктор класса Ball необходимо передать аргумент

// для параметра radius

С = color;

}

}

Далее приводится исправленная версия класса ColoredBall, в которой требуемый аргумент передается в конструктор класса Ball:

public class ColoredBall extends Ball { private var c;

// Все исправлено…

public function ColoredBall (radius, color) { super(radius); с = color;

Обратите внимание, что, если придерживаться хорошего тона программирования, в конструкторе подкласса сначала перечисляются параметры суперкласса (в данном случае radius), а затем дополнительные аргументы конструктора подкласса (в данном случае color).

Исключение возможности расширения классов и перекрытия методов

Чтобы исключить возможность расширения класса или перекрытия метода, перед описанием класса или метода необходимо добавить атрибут final. Например, следующий код описывает класс А, не допускающий расширения:

final public class А { }

Поскольку класс А описан с использованием атрибута final, попытка расширить этот класс следующим образом:

public class В extends А { }

завершится ошибкой на этапе компиляции программы:

Base class is final. (Базовый класс является конечным.)

Подобным образом следующий код описывает метод m ( ), не допускающий перекрытия:

public class А { final public function m ( ) { }

}

Поскольку метод m ( ) описан с помощью атрибута f in а 1, попытка перекрыть этот метод следующим образом:

public class В extends А { override public function m ( ) { }

}

завершится ошибкой на этапе компиляции программы:

Cannot redefine a final method. (Невозможно переопределить конечный метод.)

В языке ActionScript атрибут final используется по нескольким причинам.

? В некоторых ситуациях методы, описанные с помощью атрибута final, выполняются быстрее, чем методы, описанные без него. Если вы хотите улучшить производительность вашего приложения всеми возможными способами, попробуйте описать методы, используя атрибут final. Однако стоит отметить, что в будущих версиях среды выполнения Flash корпорация Adobe планирует увеличить скорость выполнения методов, описанных без использования атрибута final, в результате чего она не будет отличаться от скорости выполнения методов, описанных с помощью атрибута final.

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

? Методы, описанные с помощью атрибута fin а 1, помогают скрыть детали внутренней реализации класса. Если описать класс или метод, используя этот атрибут, другие программисты не смогут расширить такой класс или перекрыть метод с целью изучения внутренней структуры класса. Такая мера предосторожности является одним из способов защиты приложения от вредоносного кода.

Если оставить в стороне проблемы эффективности и обеспечения безопасности, сообщество программистов разделилось на два лагеря в обсуждении вопроса, является ли описание методов и классов с помощью атрибута final хорошей практикой объектно-ориентированного программирования. С одной стороны, часть программистов утверждает, что методы и классы, описанные с помощью атрибута final, являются полезными, поскольку в данном случае можно быть уверенным в том, что объект будет вести себя в соответствии с четко определенным поведением, а не в соответствии с непредсказуемым (и потенциально проблематичным) перекрытым поведением. В то же время другие программисты утверждают, что методы и классы, описанные с помощью атрибута final, противоречат основному принципу объектно-ориентированного программирования — полиморфизму, который предполагает, что экземпляр подкласса может быть использован везде, где допустимо применение его суперкласса. С полиморфизмом мы познакомимся далее в этой главе.

Создание подклассов внутренних классов

Точно так же, как мы создаем подклассы для наших собственных классов, мы можем создавать подклассы для любого внутреннего класса, описанного без использования атрибута final, что позволит реализовать специализированную функциональность на базе существующего класса языка ActionScript. Пример расширения внутреннего класса Array можно найти в разделе Programming ActionScript 3.0 > Core ActionScript 3.0 Data Types and Classes > Working with Arrays > Advanced Topics документации по программированию на языке ActionScript 3.0 корпорации Adobe. Пример расширения внутреннего класса Shape среды выполнения Flash можно найти в разд. «Пользовательские графические классы» гл. 20.

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

Некоторые внутренние классы языка ActionScript представляют собой простые коллекции методов и переменных класса, например классы Math, Keyboard и Mouse существуют только для хранения связанных методов и переменных (например, Math. random ( ) и Keyboard. ENTER). Такие классы называют библиотеками статических методов. Они объявляются в основном с использованием атрибута final.

Вместо того чтобы расширять эти классы, вы должны создавать свои собственные библиотеки статических методов. Например, вместо добавления метода factorial( ) в подкласс класса Math следует создать собственный класс, скажем, AdvancedMath, который будет хранить ваш метод factorial ( ). Класс AdvancedMath не может быть связан с классом Math через отношение наследования.

Теория наследования

166

Теория наследования

До настоящего момента основное внимание уделялось рассмотрению деталей практического применения наследования в языке ActionScript. Однако теория о том, где и когда применять наследование, гораздо глубже технической реализации. Рассмотрим несколько основных теоретических принципов, принимая во внимание тот факт, что для освещения этой темы целиком нескольких страниц явно недостаточно. Более подробное описание теории наследования можно найти по адресу http://archive. eiffelxom/doc/manuals/technology/oosc/inheritance-design^ page. html. Страница представляет собой онлайновую выдержку из книги «Object-Oriented Software Construction (издательство Prentice Hall) Бертранда Мейера (Bertrand Meyer).

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

Почему наследование?

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

Кроме того, наследование позволяет выражать архитектуру приложения в иерархических терминах, отражающих реальный мир и человеческую психологию. Например, в реальном мире мы считаем, что растения отличаются от животных, но в то же время и тех и других мы относим к живым существам. Мы считаем, что автомобили отличаются от самолетов, но и те и другие являются средством передвижения. Соответствующим образом в приложении для управления кадрами может существовать суперкласс Employee с подклассами Manager, CEO и Worker. В банковском приложении можно создать суперкласс BankAccount с подклассами CheckingAccount и SavingsAccount. Все эти канонические примеры демонстрируют одну из разновидностей наследования, иногда называемую наследованием подтипов, когда иерархия классов приложения моделирует ситуацию в реальном мире (называемую доменом или проблемной областью).

Несмотря на то что примеры классов Employee и BankAccount демонстрируют привлекательные возможности наследования, далеко не каждое наследование отражает реальный мир. На самом деле чрезмерный акцент на моделировании реального мира может привести к неправильному пониманию наследования и, как следствие, к его неправильному использованию. Например, в случае с классом Person мы могли бы поддаться искушению и создать подклассы Female и Male. В реальном мире данные категории являются логичными, но если бы эти классы использовались, скажем, в приложении для генерации отчетов в учебном заведении, нам пришлось бы создать классы MaleStudent и Female Student только для того, чтобы сохранить иерархию реального мира. В нашей программе операции,

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

Описанные для студентов мужского пола, ничем не отличаются от операций, описанных для студентов женского пола, и, следовательно, должны использоваться одинаково. В данном случае иерархия реального мира конфликтует с иерархией нашего приложения. Если вам необходима информация о поле, лучше создать один класс Student и добавить переменную gender в класс Person. Насколько бы это ни было заманчивым, мы должны избегать создания структур наследования, основываясь исключительно на реальном мире, а не на требованиях нашей программы.

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

Полиморфизм и динамическое связывание

Полиморфизм — это возможность, присущая всем настоящим объектно-ориентированным языкам программирования, которая заключается в том, что экземпляр подкласса может быть использован везде, где допустимо применение экземпляра его суперкласса. Само по себе слово «полиморфизм» буквально обозначает «множество форм» — любой объект можно рассматривать как экземпляр собственного класса или как экземпляр любого из его суперклассов.

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

В качестве канонического примера полиморфизма и динамического связывания можно привести графическое приложение, отображающее фигуры на экране. В этом приложении определен класс Shape с нереализованным методом draw ( ):

public class Shape { public function draw ( ) { // Реализация метода отсутствует. В некоторых других языках // метод draw( ) был бы объявлен с помощью атрибута abstract, // который синтаксически обязует подклассы класса Shape // предоставить реализацию данного метода.

}

}

Класс Shape имеет несколько подклассов — Circle, Rectangle и Triangle, каждый из которых предоставляет собственное описание метода draw ( ):

public class Circle extends Shape { override public function draw ( ) { // Код для отрисовки окружности на экране не показан…

}

}

public class Rectangle extends Shape { override public function draw ( ) {

// Код для отрисовки прямоугольника на экране не показан…

}

}

public class Triangle extends Shape { override public function draw ( ) { // Код для отрисовки треугольника на экране не показан…

}

}

Чтобы нарисовать новую фигуру на экране, мы передаем экземпляр класса Circle, Rectangle или Triangle в метод addShape ( ) основного класса приложения DrawingApp. Код метода addShape ( ) класса DrawingApp выглядит следующим образом:

public function addShape (newShape) { newShape. draw( );

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

}

Теперь рассмотрим пример добавления фигуры, представленной классом Circle, на экран:

drawingApp. addShape(new Circle( )):

Метод addShape ( ) вызывает метод draw ( ) для новой фигуры и добавляет эту фигуру во внутренний список фигур, отображаемых на экране. И самое главное — метод addShape ( ) вызывает метод draw ( ), не зная (и не заботясь о том), экземпляром какого класса является новая фигура — Circle, Rectangle или Triangle. Благодаря процессу динамического связывания, происходящему на этапе выполнения программы, среда Flash использует подходящую реализацию данного метода. Иными словами, если новая фигура является экземпляром класса Circle, то среда выполнения Flash вызовет метод Circle. draw ( ); если новая фигура является экземпляром класса Rectangle, то Flash вызовет метод Rectangle. draw ( ); если же новая фигура является экземпляром класса Triangle, то среда Flash вызовет метод Triangle. draw ( ). Важно отметить, что на этапе компиляции конкретный класс добавляемой фигуры неизвестен. По этой причине динамическое связывание часто называют поздним связыванием: вызов метода связывается с конкретной реализацией «с опозданием» (то есть на этапе выполнения).

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

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

public class Circle extends Shape { public function drawCircle ( ) {

// Код для отрисовки окружности на экране не показан…

}

}

public class Rectangle extends Shape { public function drawRectangle ( ) { // Код для отрисовки прямоугольника на экране не показан…

}

}

public class Triangle extends Shape { public function drawTriangle ( ) { // Код для отрисовки треугольника на экране не показан…

}

}

Далее внутри метода addShape ( ) класса DrawingApp нам бы пришлось использовать оператор is, чтобы вручную определять класс каждой новой фигуры и вызывать подходящий метод для отрисовки, как показано в следующем коде. Оператор is возвращает значение true в том случае, если указанное выражение принадлежит заданному типу данных; в противном случае возвращается значение false. Типы данных и оператор is будут рассмотрены в гл. 8.

public function addShape (newShape) { if (newShape is Circle) {

newShape. drawCircle( ); } else if (newShape is Rectangle) {

newShape. drawRectangle( ); } else if (newShape is Triangle) {

newShape. drawTriangle( );

}

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

}

Уже сейчас очевидны трудности выбранного подхода. Теперь представьте, что произойдет, если мы добавим 20 новых типов фигур. Для каждого нового типа нам придется вносить изменения в метод addShape( ). В мире, где существует полиморфизм, нам не пришлось бы изменять код, вызывающий метод draw ( ) над каждым экземпляром класса Shape. Поскольку каждый подкласс класса Shape предоставляет собственное подходящее описание метода draw ( ), наше приложение будет «просто работать» без необходимости внесения других изменений.

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

Полиморфизм не только упрощает взаимодействие между программистами, но и позволяет применять и расширять библиотеки, не требуя при этом доступа к их исходному коду. Некоторые разработчики утверждают, что полиморфизм — это величайший вклад объектно-ориентированного программирования в компьютерную науку.

Наследование в сравнении с композицией

Композиция, представляющая альтернативный вид отношений между объектами, зачастую соперничает с наследованием в качестве методики объектно-ориентиро-

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

// Внутренний класс является аналогом суперкласса в наследовании public class BackEnd {

public function doSomething ( ) {

}

}

// Внешний класс является аналогом подкласса в наследовании public class FrontEnd {

// Экземпляр внутреннего класса сохраняется в закрытой переменной

// экземпляра, в данном случае в закрытой пременной be

private var be;

// Конструктор создает экземпляр внутреннего класса public function FrontEnd ( ) { be = new BackEnd( );

}

// Этот метод поручает работу методу doSomething( ) класса BackEnd public function doSomething ( ) { be. doSomething( );

}

}

Обратите внимание, что класс FrontEnd не расширяет класс BackEnd. Композиция не требует использования собственного особого синтаксиса, как это происходит с наследованием. Более того, внешний класс может использовать подмножество методов внутреннего класса, все методы или добавлять собственные несвязанные методы. Имена методов во внешнем классе могут полностью совпадать с именами методов во внутреннем классе или совершенно отличаться. Внешний класс может ограничивать, расширять или переопределять возможности внутреннего класса, играя такую же роль, как подкласс в наследовании.

Ранее в этой главе было описано, как с помощью наследования класс Square может ограничивать поведение класса Rectangle. В листинге 6.2 показано, как одно и то же отношение между классами может быть реализовано с использованием композиции вместо наследования. В предлагаемом коде класс Rectangle остался неизмененным. Однако на этот раз класс Square не расширяет Rectangle. Вместо этого в классе Square описана переменная г, содержащая экземпляр класса Rectangle. Фильтрация всех операций над экземпляром, хранящимся в переменной г, происходит через public-методы класса Square. Класс Square переадресует, или делегирует, вызовы методов экземпляру, хранящемуся в переменной г. Обратите внимание, что, поскольку метод setSize ( ) класса Square не перекрывает метод setSize ( ) класса Rectangle, сигнатура метода setSize ( ) класса Square не обязана совпадать с сигнатурой метода setSize ( ) класса Rectangle. В методе setSize ( ) класса Square можно определить один-единственный параметр, в отличие от метода setSize ( ) класса Rectangle, в котором определено два параметра.

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

Листинг 6.2. Пример отношения композиции

// Класс Rectangle public class Rectangle {

protected var w = 0;

protected var h = 0;

public function Rectangle (width, height) { setSize(width, height);

}

public function setSize (newW, newH) { w = newW; h = newH;

}

public function getArea ( ) { return w * h;

}

}

// Это новый класс Square public-class Square { private var r;

public function Square (side) { r = new Rectangle(side, side);

}

public function setSize (side) { r. setSize(side, side);

}

public function getArea ( ) { return r. getArea( );

}

}

Отношения «является», «имеет» и «использует». В разговорной речи отношение наследования, присущее объектно-ориентированным языкам программирования, называется отношением «является» (Is-А), поскольку экземпляр подкласса в буквальном смысле можно рассматривать как экземпляр его суперкласса (то есть экземпляр подкласса может быть использован везде, где это допустимо применением экземпляра его суперкласса). В предыдущем примере полиморфизма экземпляр класса Circle «является» экземпляром класса Shape, поскольку класс Circle унаследован от Shape и, следовательно, может использоваться везде, где используется Shape.

Отношение композиции называется отношением «имеет», поскольку внешний класс содержит экземпляр внутреннего класса. Не следует путать отношение «имеет» с отношением «использует», когда некоторый класс создает объект другого класса, но не присваивает созданный объект переменной экземпляра. В отношении «использует» класс использует объект, а затем выбрасывает его. Например, класс Circle мо-

жет хранить числовое значение цвета в переменной color («имеет» объект класса uint), но впоследствии может временно воспользоваться объектом класса Color, чтобы отобразить этот цвет на экране («использует» объект класса Color).

В листинге 6.2 класс Square «имеет» экземпляр класса Rectangle п налагает на него ограничения, которые фактически превращают класс Rectangle в Square. В случае с классами Square и Rectangle отношение «является» выглядит более естественным, однако можно использовать и отношение «имеет». В этой связи возникает вопрос: какое отношение лучше?



Полезные ссылки
Случайные записи
  • 10.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.47
  • 28.08.2011">Занимайся спортом стар и млад!
  • 13.03.2011">Руководство по actionscript. часть 3, стр. 105
  • 18.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.96
  • 10.03.2011">Руководство по actionscript. часть 4, стр. 032
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.30
  • 29.02.2012">«Яндекс.Диск» — российский ответ сервису iCloud от Apple
  • 17.03.2011">Руководство по actionscript. часть 2, стр. 154
  • 03.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.71
  • 03.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.67
  • 28.07.2011">Что мешает росту ТИЦ?
  • 04.03.2011">Руководство по actionscript. часть 5, стр. 031
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 048
  • 03.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.96
  • 22.01.2011">Руководство по actionscript. часть 1, стр. 113
Опрос

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

View Results

Loading ... Loading ...