Flash Ripper RSS Readers

ActionScript 2.0 для пользователей ActionScript 1.0: Часть третья. Наследование и интерфейсы

Автор: Joey Lott [http://www.person13.com/]

В первых двух частях [часть 1 | часть 2] мы рассматривали структуру и синтаксис классов в ActionScript 2.0. Мы узнали, как создавать классы в ActionScript 2.0, включая использование пакетов, членов класса с разными режимами доступа (public/private/static), а также геттеров и сеттеров. Теперь поговорим о наследовании (inheritance) и интерфейсах (interfaces).

О наследовании

Наследование связано с иерархичностью классов. Можно определить подкласс и суперкласс. Подкласс автоматически наследует всех членов суперкласса без необходимости их явного повторного определения. Это важное и полезное свойство классов, поскольку позволяет использовать высокий уровень абстракции. А высокий уровень абстракции ведет к высокой эффективности рабочего процесса, и чем сложнее проект, тем это заметнее.

Например, вы хотите создать класс Car, подобный тому, что мы создавали в предыдущих частях статьи. Ясно, что Автомобиль — это один из многих типов Машины, помимо Автомобилей существуют Вертолеты, Дрезины, Джипы, Терминаторы и т.д. Хотя Машины и отличаются друг от друга, у них есть много общего. Все вышеперечисленные машины могут иметь такие свойства, как "грузоподъемность" или "пробег" (для Самолетов — "пролет"). А если Машина еще и передвигается в пространстве, у нее есть методы для перемещения с заданной скоростью.

Так вот, если вы создадите суперкласс Vehicle (т. е. "Машина"), то можно в него поместить все общие для машин характеристики (свойства и методы). При этом код будет написан только в одном месте, а использовать его можно будет во многих местах: во всех подклассах класса Машины. Для создания суперкласса не нужно быть чемпионом по программированию среди домохозяек (или домохозяинов). Просто берете и создаете суперкласс, как раньше создавали обычные классы. А вот чтобы наследовать от этого класса (иными словами, расширить его), потребуются новая техника. Довольно легкая в освоении.

Расширение суперкласса

В терминах ООП можно сказать, что подкласс расширяет (extends) суперкласс. В нашем примере подкласс Car расширяет суперкласс Vehicle. Чтобы Flash создал такую связь между ними, нужно при объявлении подкласса использовать ключевое слово extends:

class Subclass extends Superclass {
}

В случае с Автомобилем и Машиной получится приблизительно следующее:

class Car extends Vehicle {
}

Вы всего один раз говорите, что класс Car расширяет класс Vehicle, и получаете при этом в свое распоряжение все свойства и методы класса Vehicle, как будто бы они были определены в классе Car.

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

Часто возникает ситуация, когда в подклассе необходимо унаследовать метод суперкласса без каких-либо изменений в этом методе. В таких случаях в определением подкласса просто не нужно ничего делать с этими методами. Но во многих других случаях метод суперкласса может быть перекрыт — для реализации в данном методе особенностей подкласса. Это называется перекрытием (overriding) метода суперкласса, поскольку при этом подкласс определяет в себе собственную реализацию метода, которая перекрывает унаследованную, родительскую.

Перекрывать методы суперкласса можно по-разному:

  1. Вы можете перекрыть метод полностью, так что реализация метода в суперклассе будет полностью проигнорирована;
  2. Можно сделать так, чтобы по-прежнему вызывался метод суперкласса, а реализация метода в подклассе добавляла бы всю нужную дополнительную функциональность.

Чтобы полностью перекрыть родительский метод, просто определите в классе-потомке метод с точно таким же именем, как и у родительского метода. Например, если у суперкласса есть метод типа:

// Метод определен в суперклассе
public function display():Void {
trace("Это метод суперкласса");
}

Потом можно определить реализацию метода в подклассе, которая перекроет реализацию этого метода в суперклассе:

// Метод определен в подклассе
public function display():Void {
trace("Это метод подкласса");
}

Это был первый подход.

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

// Метод определен в подклассе, происходит вызов метода суперкласса
public function display():Void {
super.display();
trace("Это метод подкласса");
}

Затем, если из экземпляра подкласса вызвать метод display() подкласса, в панели Output увидим следующее:

Это метод суперкласса
Это метод подкласса

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

Создание классов Машины и Автомобиля

Конец теории. Выполним упражнение. Создадим суперкласс Vehicle (Машина) и подкласс Car (Автомобиль). Если вы сделали упражнение из второй части данной статьи, то вам будет легко следить за мыслью.

  1. Создайте структуру директорий для пакета com.person13. Например, D:\Swf\Classes\com\person13;
  2. Откройте новый ActionScript-файл;
  3. Добавьте в него такой код:
    class com.person13.Vehicle {
    private var _nPassengers:Number;
    private var _nMiles:Number;
    private var _nInterval:Number;
    function Vehicle(nPassengers:Number, nMiles:Number) {
    _nPassengers = nPassengers;
    miles = nMiles;
    }
    public function get passengers():Number {
    return _nPassengers;
    }
    public function get miles():Number {
    return _nMiles;
    }
    public function set miles(nMiles:Number):Void { if(nMiles >= 0) {
    _nMiles = nMiles;
    }
    else {
    _nMiles = 0;
    }
    }
    public function move(nMPH:Number):Void {
    if(nMPH == null || nMPH <= 0) {
    clearInterval(_nInterval);
    }
    else {
    _nInterval = setInterval(this, "increment", 1000, nMPH);
    }
    }
    private function increment(nMPH:Number):Void {
    _nMiles += nMPH;
    }
    }
  4. Сохраните файл как Vehicle.as в директории com/person13;
  5. Откройте файл класса Car.as (мы создавали его во второй части) (по идее он должен был остаться в директории com/person13 ). В противном случае откройте новый ActionScript-файл;
  6. Добавьте в файл Car.as код (или напишите заново, если вы все же поленились сделать упражнение из второй части). Должно получиться так:
    // Класс Car расширяет класс Vehicle и наследует все его свойства и методы
    class com.person13.Car extends com.person13.Vehicle{
    // Объявляем только свойства, специфические для класса Car.
    // Остальные свойства наследуются, так что не надо объявлять их заново.
    private var _sMake:String;
    private var _sModel:String;
    private var _nYear:Number;
    function Car(sMake:String, sModel:String, nYear:Number, nPassengers:Number, nMiles:Number) {
    // Вызываем конструктор суперкласса, затем присваиваем значения
    // свойств, специфических для класса Car.
    super(nPassengers, nMiles);
    _sMake = sMake;
    _sModel = sModel;
    year = nYear;
    }
    public function get make():String {
    return _sMake;
    }
    public function get model():String {
    return _sModel;
    }
    public function get year():Number {
    return _nYear;
    }
    public function set year(nYear:Number):Void {
    if(nYear >= 1886) {
    _nYear = nYear;
    }
    else {
    _nYear = 1886;
    }
    }
    // Определяем метод drive(), который на самом деле вызывает метод move(),
    // унаследованный из суперкласса Vehicle. Именно так делать не обязательно, но
    // лучше все же делать так, самому же потом разобраться легче будет,
    // поскольку термин "drive" больше подходит для машины, чем "move".
    // Таким образом, API для класса Car будет интуитивно понятнее.
    public function drive(nMPH:Number):Void {
    move(nMPH);
    }
    }
  7. Сохраните файл Car.as в директории com/person13, где перед этим сохраняли Vehicle.as;
  8. Возьмите .fla-файл, созданный ранее во второй части. Или создайте новый .fla-файл;
  9. Как бы там ни было, в первом кадре этого .fla-файла должен оказаться такой код:
    import com.person13.*;
    function displayMileage(carObj:Car):Void {
    trace(carObj.miles);
    }
    var car:Car = new Car("Oldsmobile", "Alero", 2000, 5, 43000);
    car.drive(65);
    setInterval(displayMileage, 100, car);
  10. Теперь можно делать Test Movie. В панели Output вы увидите пробег автомобиля в милях. Число будет увеличиваться раз в секунду, потому что автомобиль едет.

    [Необязательная часть для любителей воздушных аппаратов]

  11. Откройте новый ActionScript-файл;
  12. Добавьте в него такой код:
  13. class com.person13.Plane extends com.person13.Vehicle{
    private var _sMake:String;
    private var _sModel:String;
    function Plane(sMake:String, sModel:String, nPassengers:Number, nMiles:Number) {
    super(nPassengers, nMiles);
    _sMake = sMake;
    _sModel = sModel;
    }
    public function get make():String {
    return _sMake;
    }
    public function get model():String {
    return _sModel;
    } public function fly(nMPH:Number):Void {
    move(nMPH);
    }
    }
  14. Сохраните файл как Plane.as в директории in the com/person13;
  15. Создайте новый .fla-файл;
  16. Добавьте такой код в первый кадр:
    import com.person13.*;
    function displayMileage(obj:Plane):Void {
    trace(obj.miles);
    }
    var plane:Plane = new Plane("Boeing", "747", 300, 100000);
    plane.fly(400);
    setInterval(displayMileage, 100, plane);
  17. Теперь — TestMovie.

Расширение подклассов

Вы не ограничены одним уровнем наследования. Подкласс тоже можно расширять. Также как и подкласс подкласса-подкласса-подкласса -подкласса. Например, можно создать класс Niva, который будет расширять класс Car, или создать класс Kukuruzniq, расширяющий класс Plane. Процесс создания таких подклассов аналогичен описанной выше последоватьельности действий и не зависит от наличия суперкласса у расширяемого подкласса.

Об интерфейсах

Интерфейсы — это просто. Это набор правил, в соответствии с которыми реализуется некий набор классов. Когда вы определяете класс, который следует правилам интерфейса, то говорят, что класс реализует (иногда на жаргоне говорят — "имплементит", от англ. "implements") интерфейс.

Какие правила определяются в интерфейсе? Интерфейс в ActionScript 2.0 сообщает Flash, какие методы должны реализоваться в классе, включая следующую информацию:

  • Имя метода;
  • Входные параметры;
  • Публичное объявление метода (объявления методов с режимом доступа private или static в интерфейсе недопустимы);
  • Тип возвращаемого значения.

В интерфейсе недопустимы:

  • Объявления свойств;
  • Реализации методов;
  • Объявления методов с режимами доступа private или static.

Затем можно реализовывать интерфейс в классе. При этом, кроме всего прочего, в классе должны реализовываться методы, объявленные в интерфейсе. В противном случае на этапе компиляции Flash выдаст сообщение об ошибке.

Преимущества интерфейсов

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

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

Например, вы работаете в команде, которая создает классы Vehicle (Машина), Animal (Животное), Wind (Ветер) и Planet (Планета). В то время как у каждого из этих классов может быть собственный API, между ними есть и общее — все они способны двигаться. Если вы хотите реализовать движение в каждом из этих классов, можно создать интерфейс IMoveable, который будет реализовываться всеми этими классами. Интерфейс IMoveable будет содержать объявление метода move(), так что теперь каждый конкретный человек, работающий над реализацией конкретного класса, будет учитывать этот факт в процессе разработки и реализует метод move() в разрабатываемом им классе. Это большой плюс при командной работе. Когда вы создаете интерфейс и определяете, какие классы будут его реализовать, то потом все разработчики, работающие над классом, будут заранее знать, какие методы писать, и это облегчает стандартизацию API всех классов. Еще одно преимущество получает разработчик, использующий такой класс. Зная, какой именно конкретный интерфейс реализуется в классе, вы ужен знаете много о своем API.

Один класс может реализовывать многочисленные интерфейсы. Поэтому интерфейсы часто используются как форма множественного наследования в языках, изначально не поддерживающих такой вид наследования. В ActionScript подкласс может наследовать только от одного суперкласса. Например, класс Car расширяет класс Vehicle, и следовательно не может расширять ни один другой класс. Но Car может реализовывать множественные интерфейсы (продолжая при этом наследовать от класса Vehicle.) Например, Car может реализовать интерфейсы типа IDrivable (автомобиль можно водить), IManufacturable (можно производить), ISellable (можно продать).

Создание интерфейса

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

Декларация (объявление) интерфейса выглядят почти идентично декларации класса. Единственное различие в том, что при объявлении интерфейса используется ключевое слово interface, вместо ключевого слова class. Например:

interface IMoveable {
// Определение интерфейса
}

В интерфейсе можно только объявлять методы, но не реализовывать их. Реализовываться методы будут в классах, реализующих интерфейс. В интерфейсе нельзя также объявлять свойства. Еще раз кратко перечислим то, что может и должно содержаться в интерфейсе:

  • Имя метода;
  • Имя и тип ожидаемого параметра;
  • Объявление публичных методов;
  • Тип возвращаемого значения.

Вот пример готового интерфейса IMoveable:

interface IMoveable {
public function move(nMPH:Number):Void;
}

Как и классы, интерфейсы удобно хранить в пакетах. Например, можно упаковать IMoveable в пакет com.person13. В декларации интерфейса это выглядит вот так:

interface com.person13.IMoveable {
public function move(nMPH:Number):Void;
}

И также, как и классы, интерфейсы могут расширять друг друга Например, IDrivable может расширять IMoveable, вот как будет при этом выглядеть код:

import com.person13.IMoveable
interface com.person13.IDrivable extends IMoveable {
public function drive(nMPH:Number):Void;
}

Реализация интерфейса

После определения интерфейса можно указать, что некий класс реализует этот интерфейс. Для этого используется ключевое слово implements, за которым будет следовать список разделенных запятыми реализуемых интерфейсов. Например, если класс Vehicle реализует интерфейс IMoveable, то начало объявления класса будет выглядеть так:

class com.person13.Vehicle implements com.person13.IMoveable {

Конечно, при этом можно пользоваться импортом (как и с классами):

import com.person13.IMoveable;
class com.person13.Vehicle implements IMoveable {

А вот класс Car реализует интерфейсы IDrivable, IManufacturable, и ISellable:

import com.person13.*;
class com.person13.Car implements IDrivable, IManufacturable, ISellable {

Когда класс реализует интерфейс, Flash будет проверять правильность этой реализации во время компиляции. И если какой-то метод пропущен или объявлен неверно, не так, как в интерфейсе, то Flash выдаст сообщение об ошибке.

(От переводчика: таким образом, легко понять мощь компиляции. Интерфейсы являются таковыми прежде всего для классов, прокладывая между "городами" разных классов "дороги" объявленных в интерфейсе методов. Как будто разные классы (читайте — разработчики разных классов) подписали виртуальный договор о сотрудничестве. После подписания такого договора каждый класс-участник договора обязан реализовать все объявленные в договоре-интерфейсе методы. А поскольку "за исполнением условий договора" следит сама среда разработки Flash, на этапе компиляции указывающая на любые отклонения от договора, то можно не сомневаться в соблюдении условий. Гениальное изобретение!)

Вывод

Вот и разобрались. Комментарии приветствуются как никогда. Спасибо :]

Примечания:

1) Часто вместо "перекрытие" говорят "переопределение". Пусть это вас не смущает!

2) Не забывайте, что иногда вместо "реализовать" говорят "имплементить".

Писал Rost, 3 Октябрь 2003 3:06

Найдены баги:

Огромное спасибо!
Очень хорошие материалы.
С уважением и пожеланиями

asd - 3 Октябрь 2003 22:29

Рост, англицизм "имплементит" если и используют, то не часто.
Вообще не понимаю зачем использовать такие уродливые кальки тогда, когда есть нормальный (и общеупотребительный, кстати)русскоязычный эквивалент слову "implements".

Max - 5 Октябрь 2003 13:41

Wow! Не знаю что должно сказать! Очень очень спасибо :)
Золотые материалы.
С уважением!

Sultan - 6 Октябрь 2003 13:44

2Max:

Я подумал и решил, что действительно можно обойтись без жаргона. Оставил примечание. Спасибо!

Рост - 6 Октябрь 2003 15:12

2Рост:
Как мне заставить подкласс Car расширять суперкласс Vehicle и имплементить интерфейс IDrivable одновременно ( то есть в одном строке )?

Спасибо заранее :)

Sultan - 9 Октябрь 2003 6:48

Hello!

Sultan - 10 Октябрь 2003 13:02

Опечатка у переводчика или автора:
"...Это был первый подход.
Если же вы хотите реализовать в подклассе метод, который будет вызывать также свою реализацию в подклассе, можете использовать ключевое слово super..."

Вместо последнего слова "подклассе" д.б. слово "суперклассе".

Смольный - 11 Октябрь 2003 18:23

2Смольный: Спасибо за замечание, ошибку исправил!

Рост - 13 Октябрь 2003 11:33

Спасибо, зачитал все три статьи. Если бы не они, так бы и думал, что программирование даже на новом флеше - полная задница :)

Leonid - 20 Сентябрь 2004 14:49

Спасибо но еслибы была рассылка статей на почту былобы ещё круче

ALEXS - 14 Декабрь 2004 14:27

Рассылку новостей сделаем. В ближайшем будущем.

Голос из зала - 14 Декабрь 2004 17:20

а теперь ваще умный стал

assimetric - 11 Январь 2005 16:12

а теперь ваще умный стал

assimetric - 11 Январь 2005 16:12



Это запись из категории 'Coding'. 10 еще cвежих:

Архивы по категориям:

3D-18, Adobe AIR-30, Animation-1, Apache Ant-1, Architecture-1, ARP-1, Art-25, Articles-26, AS3-52, Books-7, Business-3, Cairngorm-2, CI-1, Classes-10, Coding-30, Community-113, Components-19, Contests-28, Cool-Job-5, Debug-18, Design-26, Development-84, EMO-1, Events-13, Extensions-2, FAQ-8, FDS-1, Flash and html-5, Flash Player-35, Flash Updates-8, Flash-scene-1, flash10-4, FlashLite-2, Flex-30, Flex 2-80, Flickr-1, FMS-1, FPUG-46, frameworks-1, Games-11, Good Job!-35, HaXe-14, Health-2, Humor-10, Ideas-13, JavaScript-1, Job-26, JSFL-8, Links-2, Linux-1, Maps-1, Math-8, Money-11, MXML-1, Open Source-15, Optimization-2, Patterns-2, Personalities-27, Politics-1, Preloading-3, Productivity-9, PureMVC-10, Pv3d-1, Rafpug-4, Red5-3, Remoting-11, Resources-21, Ruby-6, SAAS-1, Security-11, SEO-8, Silverlight-5, Sound-3, Strategy-120, Tamarin-1, Tools-113, Training-2, Trash-8, URAFPUG-13, Urgent-1, Usability-6, Video-6, VoIP-5, Wallop-1, Wishlist-2, Архив всех записей (большой)

За последние месяцы:

Июл 2008: Международная встреча разработчиков URAFPUG завершена, URAFPUG - трансляция студии Flex-фреймворка Mate, весь Июл

Июн 2008: Попытка предварительных выводов о встрече аниматоров, Онлайн трансляция встречи аниматоров в Донецке, весь Июн

Май 2008: Если 3D, то по-взрослому: официальный запрос в Adobe по поводу контроля над мип-маппингом. Нужна ваша поддержка!, В этом году «Russian Flash Awards» пройдет в «космическом стиле», весь Май

Апр 2008: Программирование под флэш платформу. Cтатья (местами спорная), Advanced Flash Components бесплатно раздает все свои AS2-компоненты, весь Апр

Мар 2008: Зарплаты программистов в 2007 году, FlashPhone как технология года? Технология года? В Рунете?, весь Мар

Фев 2008: ЙА ФПУГ — регистрация на первую встречу UAFPUG продолжается, Закулисы Flex и секрет успеха опенсорс-проекта, весь Фев





Примечания:
Статус документа
: в процессе
   2002-2007 Производство: Рост Прибыли · О проекте · Подписка на новости (RSS)