13 июня 2013 г.

Основы ООП для ActionScript 3.0

Всем привет!
Решил выложить свою старую статью, которую готовил для тематической группы Вконтакте. Для многих данная статья, не откроет ничего нового, так как здесь описаны фундаментальные вещи объектно-ориентированного программирования в ActionScript 3.0 и рассчитана для самых маленьких. Надеюсь для мира она будет полезна =)

Конечно лектор из меня никудышный, так что сильно не гнобите если что…

Приступим. Первое что приходит в голову – это что такое объектно-ориентированное программирования. Открываем браузер, набираем: http://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование и внимательно читаем.



Если вы ничего не поняли с выше прочитанного – это нормально!

Как сказано на странице Википедии, ООП базируется на объектах и соответствующих им классах.

Значит класс, что это и с чем его едят. Классом можно назвать описания типа данных. Т.е. такие вещи как MovieClip, Button это тоже классы. Класс описывает внутреннее строения объекта, все его свойства и функции.

Например, у нас есть коробка спичек. Что мы можем о ней сказать? Она имеет определённый размер: длинна, ширина, высота, она имеет определённый цвет: серый, она имеет определённый текст (реклама на задней стороне коробки), она имеет внутри определённое количество спичек. Также можно сказать, что коробка может выполнять некоторые функции, например: открываться и закрываться. Примерно так мы представляем эту коробку в реальном мире!

Для того чтобы описать нашу коробку языком ООП давайте разберёмся с некоторыми определениями.

Свойство – являет собой переменную некого конкретного типа.
Метод – функция выполняющая определённое действие. Может принимать параметры, а может, и нет.Может возвращать какие-то данные, а может, и нет.

Данную коробку можно описать в классе. Пример:
Название класса: коробка
Свойства: ширина, высота, длинна, цвет, текст, количество спичек.
Методы: Открываться, закрываться.
Представим это в коде:
class Box
{
        var width:Number = 10;
        var height:Number = 10;
        var depth:Number = 10;
        var text:String = "Реклама";
        var color:Number = 0x000000;
        var matchCount:int = 3;

        function Box()
        {}
        function Open():void
        {
        }
        function Close():void
        {
        }
}

Получаем что-то в этом роде. Не спешите это всё писать.

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

Замечание: Хорошим тоном программирования считается написание имен классов и методов с большой буквы. Имена свойств с маленькой. Также приветствуется написание всех идентификаторов (имен) в смысловом значении, без всяких знаков подчёркивания итд. Пример: MamaMilaRamu а не Mama_mila_ramu, MyNameIs а не My_nameis. Такой подход улучшает читабельность кода.

Давайте для наглядности создадим ещё один класс – спичка:
class Match
{
        var length:Number = 10;
        var color:Number = 0xffffff;
        var burned:Boolean = false;

        function Match()
        {}

        function Burn():void
        {
                burned = true;
        }
}

Я думаю, что значения свойств и методов данного класса объяснять не нужно.

Таким образом, мы имеем 2 класса Box и Match.Попробуем создать в классе Box, объект Match. Перепишем наш класс Box:
class Box
{
        var width:Number = 10;
        var height:Number = 10;
        var depth:Number = 10;
        var text:String = "Реклама";
        var color:Number = 0x000000;
        var matchCount:int = 1;

        var match:Match = new Match();

        function Box()
        {
        }

        function Open():void
        {
        }
        function Close():void
        {
        }
}

Создаём новое свойство(переменную), указываем тип Match – наш класс, и присваиваем ему новый объект Match. Этот процесс присваивания называется инициализация нового объекта. За инициализацию отвечает директива new. Если взглянуть на данный процесс внутри памяти компа, то мы увидим, как под переменную match выделяется память нужного размера, в неё помещаются все наши свойства и описания методов, и запускается метод. Таким образом рождается новый объект. Если мы объявляем нашу переменную, но не инициализируем ее, то в памяти под нее отводится место, но никаких свойств, методов там нет. И соответственно конструктор в данном случае не запускается. Значение такой переменной является null, или же пустота.

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

Методы могут принимать параметры и возвращать некие данные – результат роботы метода. Пример:
function Add(mane:String):void // принимает параметр mane типа String
{
}

Или же:
function Sum(a:Number, b:Number):Number //принимает 2 параметра, суммирует и возвращает суму
{
        return a+b;
}

Слово return служит для возвращения результатов работы метода. Если же мы объявляем метод, который будет возвращать какие-либо данные, нам нужно указать тип этих данных. В противном случае указывается тип void, который значит, что ничего возвращать не будем. 

Также хочу заметить – в ООП ТИПИЗАЦИЯ ДАННЫХ ЯВЛЯЕТСЯ ОБЯЗАТЕЛЬНОЙ! 

Теперь давайте поговорим о зоне видимости наших классов, свойств и методов. В ООП, существует возможность скрывать методы и свойства, то есть, задавать зону видимости:

Public – позволяет объявить публичный (открытый для всех) метод или свойство.
Private – позволяет объявить внутренний (закрытый для всех, кроме самого себя) метод или свойство.
Protected – позволяет объявить внутренний метод или свойство, но его можно будет увидеть при наследовании класса (о наследовании немного ниже).

Как это работает, например, в нашем классе Match, есть свойство color и мы хотим чтобы к данному свойству нельзя было добраться снаружи. Для этого нам нужно объявить его как приватное (private). Если же нам нужно создать свойство, доступ к которому мог осуществляться извне, то мы объявляем публичное (public) свойство. Если же видимость не задана, то свойство или метод по умолчанию считаются приватными. Вот пример нашего класса Match:
public class Match
{
        public var length:Number = 10;
        public var color:Number = 0xffffff;
        public var burned:Boolean = false;

        public function Match()
        {}

        public function Burn():void
        {
                burned = true;
                Light();
        }

        private function Light():void
        {
                //код метода
        }
}


В данном случае, метод Light, является приватным, это означает, что мы не можем его вызвать из класса Box, но можем вызвать в классе Match.
class Box
{
        public var width:Number = 10;
        public var height:Number = 10;
        public var depth:Number = 10;
        public var text:String = "Реклама";
        public var color:Number = 0x000000;
        public var matchCount:int = 1;

        public var match:Match = new Match();

        public function Box()
        {}

        public function Open():void
        {
        }
        public function Close():void
        {
        }

        public function LightMach():void
        {
                match.Light(); // вызовет ошибку, так как метод Light() является приватным, и невидимым извне.
                match.Burn(); // запустит метод Burn() объекта match, так как он является публичным, и его можно увидеть извне.
        }
}


Ещё существует такое понятие как ссылка на самого себя – this; Что это значит? Объясняю. Иногда нужно внутри метода создать переменную с определённым именем, но в нашем классе существует свойство, которое уже имеет такое имя – в таком случае нам на выручку приходит ссылка на самого себя.
Пример:
public class Match
{
        public var length:Number = 10;
        public var color:Number = 0xffffff;
        public var burned:Boolean = false;
        public var x:Number = 10;

        public function Match()
        {}

        public function Burn():void
        {
                burned = true;
                Light();
        }

        private function Light():void
        {
                var x:Number;
                x = 30; // присваеваем значение 30 переменной x.
                this.x = 25; // присваеваем значение 24 свойчтву public var x:Number
        }
}

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

Также конструкция this служит для передачи данного объекта (самого себя) как параметра методу другого объекта.

Вот это и есть Абстракция и Инкапсуляция. Это понятно?

Теперь давайте поговорим о наследовании. Принцип наследования в ООП, аналогичен принципу наследования в нашей жизни. Ой смотрите у него папины глаза, а у неё мамин нос! Так можно сказать и в программировании.

Например у нас есть класс Box – коробка (упростим для примера наш старый Box):
public class Box
{
        public var width:Number;
        public var height:Number;
        public var depth:Number;
        public var opened:Boolean = false;

        public function Box()
        {
        }

        public function Open():void
        {
                opened = true;
        }
        public function Close():void
        {
                opened = false;
        }
}

Этот класс описывает основные свойства всех коробок в мире! То есть такое же поведение будет иметь и коробка от обуви, и от телевизора, и от холодильника. Неправда ли?

Но нам, например, нужно описать коробку для спичек и коробку для обуви и чтобы не заниматься одной и той же работой дважды, мы используем наследование. Например, выше приведённый класс Box будем считать базовым классом, и породим от него 2 класса: MatchBox и ShoesBox. Для этого нам нужно использовать при объявлении этих классов слово extends, которое указывает, что данный класс является наследником другого класса.
Пример:
public class MatchBox extends Box
{
        public function MatchBox()
        {}
}

И
public class ShoesBox extends Box
{
        public function ShoesBox ()
        {}
}

Эти оба класса унаследовали от класса Box все его свойства и методы. То есть мы можем свободно ними пользоваться как своими. Пример:
public class MatchBox extends Box
{
        public function MatchBox()
        {
                width = 15;
                height = 4;
                Open();
        }
}

Или
public class ShoesBox extends Box
{
        public function ShoesBox()
        {
        }
        private function Preview():void
        {
                Open();
        }
}

Но бывают случаи, когда нас тот или же иной метод, унаследованный от базового класса, не устраивает, и мы бы хотели его изменить. Этот процесс называется — переопределение метода, и заключается в переписывании метода с указанием слова override.
Пример:
public class MatchBox extends Box
{
        public function MatchBox()
        {
                width = 15;
                height = 4;
                Open();
        }

        override public function Open():void
        {
                if(!oppened)
                {
                        width = width*2;
                }
                opened = true;
        }
        override public function Close():void
        {
                if(oppened)
                {
                        width = width/2;
                }
                opened = false;
        }
}

Таким образом, мы переопределили (переписали) в классе MatchBox, унаследованные от базового класса Box, методы Open() и Close(). Но данные изменения относятся только к классу MatchBox, и никаких изменений в классах Box и ShoesBox не вызывают.

Для того чтобы в базовом классе создать метод, который бы нельзя было переопределить, используется слово final.

Пример:
public final function Rotate():void
{
}

Данный метод нельзя переопределить в классе потомке, так как он считается финальным.

При переопределении метода, также можно восстановить его старый код, в нужном для нас месте, за это отвечает слово super – что является ссылкой на базовый класс.

То есть в предыдущем примере, при переопределении методов, вместо строчек opened = true; и opened = false; можно написать слово super.Open() и super.Close(). Это позволит перенести весь код метода базового класса в наш переопределённый метод в нужное место.

Пример:
public class MatchBox extends Box
{
        public function MatchBox()
        {
                width = 15;
                height = 4;
                Open();
        }

        override public function Open():void
        {
                if(!oppened)
                {
                        width = width*2;
                }
                super.Open();
        }
        override public function Close():void
        {
                if(oppened)
                {
                        width = width/2;
                }
                super.Close();
        }
}

Так же при наследовании, всплывает наш старый знакомый — метод сокрития данных protected. Дело в том что если мы наследуем некие методы и свойства от некого класса, то мы сможем их увидить в классе потомке только если они будут иметь метод сокрытия public или protected.

Пример:
public class A
{
        public var x:Number = 10;
        private var y:Number = 20;
        protected var z:Number = 30;
        public function A()
        {}
}

Наследуем:
public class B extends A
{
        public function B()
        {
                x = -10; // Все работает!
                y = -20; // Получаем ошибку!
                z = -30; // Все работает!

                var objA:A = new A();
                objA.x = 100; // Все работает!
                objA.y = 200; // Получаем ошибку!
                objA.z = 300; // Получаем ошибку!
        }
}

Таким образом можна сказать что, privat — область видимости внутри класса, а protected — область видимости внутри класса и классах наследниках.

Это всё что касается наследования! Вот это и есть принципы Наследования и Полиморфизма. Здесь я думаю тоже всё понятно.

Для заметки хочу сказать, что бывают ещё такие методы, свойства и константы как статические. Это значит, что для обращения к статическому методу класса не нужно создавать объект. Например, всем известный класс Math. Для того чтобы посчитать синус числа мы пишем Math.sin(30); То есть обращаемся к методу напрямую. Такой подход служит для создания независимых от объекта методов свойств и констант.

Пример:
public class SomeMath
{
        public static const PI:Number = 3.141592;
        public static var Num:Number = 10;
        public static function Factorial(n:int):int
        {
               var result:int = 1;
                for(var i:int =1; i<n+1; i++)
                {
                        result = result * i;
                }
                return result;
        }       
}
И применение:
public class MyClass
{
        public function MyClass()
        {
                var m:Number = SomeMath.PI;
                var n:Number = SomeMath.Factorial(4);
                SomeMath.Num = m;
        }
}

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

Теперь давайте поговорим о самом интересном, о том – что флеш делает флешом! Это конечно же события! Что такое событие?

Пример. У нас есть кнопка, вот она у нас лежит, и тут в один прекрасный момент мы берём и нажимаем на неё! Вот событие и есть – «нажимаем»;

Объявление события состоит из двух частей:
1. Это само объявление (за него отвечает метод addEventListener).
2. Функция-хендл, которая представляется в виде обычного приватного метода с параметром, и запускается при срабатывании события. Хенд никогда ничего не возвращает.

Существует куча стандартных событий, они представлены в различных классах. Для примера давайте напишем код для нашей коробки – чтобы она открывалась и закрывалась по клику мышки.
package
{
        import flash.display.MovieClip;
        import flash.events.MouseEvent;

        public class Box extends MovieClip
        {
                public var opened:Boolean = false;
                public function Box()
                {
                        addEventListener(MouseEvent.CLICK, OnMouseClick);
                }
                public function Open():void
                {
                        opened = true;
                }
                public function Close():void
                {
                        opened = false;
                }
                private function OnMouseClick(t:MouseEvent):void
                {
                        if (opened)
                        {
                                Close();
                        }
                        else
                        {
                                Open();
                        }
                }
        }
}

В данном случае у нас есть класс, который унаследован от класса MovieClip. В конструкторе класса объявлен слушатель на событие MouseEvent.CLICK. Таким образом, при нажатии мышкой на данный объект выполнится хендл OnMouseClick. Параметр е, который передаётся в хендл, содержит информацию о данном событии.

Для того чтобы отменить слушание события, используется метод removeEventListener.

Пример:
removeEventListener(MouseEvent.CLICK, OnMouseClick);

Также можно создавать свои события. За это отвечает метод dispatchEvent. Но о нём вы прочитаете самостоятельно.

Так же ООП существует понятия пакета, к которому относится класс. Слово package указывает папку, в которой лежит файл класса. В данном случае он лежит в одной папке с fla файлом. Но если наш класс будет лежать в папке source/boxes/ то пакет будет указываться так: package source.boxes. Конструкция package {} является обязательной.

Слово import указывает на то, что к нашему классу подключены другие классы, лежащие в других папках.

Пример:
import flash.display.MovieClip;

Вот наверное и все основы…

Теперь снова открываем браузер, набираем: http://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование и внимательно читаем.

Есть понимание? Уже должно быть. Третий глаз начинает открываться, правда, старые 2 глаза закрываются! Шутка!

После тщательного прочтения умных вещей, давайте попробуем что-нибудь написать!

Да хоть тот самый коробок со спичками!

Для начала советую скачать программу Flash Develop и установите её. После этого открываем Flash, выбираем Flash File (ActionScript 3.0) и нажимаем Ок. Открываем установленный Flash Develop (FD) и нажимаем Projects->New Project. Выбираем в группе ActionScript 3.0 пункт Flash IDE Project, Задаем имя и сохраняем в той папке где и fla файл, созданный выше.

В правой части окна во вкладочке Project на проекте нажимаем правой кнопкой мыши на Add->NewClass. В открывшемся окне указываем имя класса (с большой буквы – для приличия) и если требуется, выбираем базовый класс, от которого будет наследоваться наш класс. В нашем случае MovieClip.

Создаём класс Box:
package{
        import flash.display.MovieClip;
        import flash.events.MouseEvent;

        public class Box extends MovieClip
        {
                public var opened:Boolean = false;
                public function Box()
                {}
                public function Open():void
                {
                        opened = true;
                }
                public function Close():void
                {
                        opened = false;
                }
        }
}

Рисуем наш спичечный коробок. Создаём из него объект MovieClip (F8). Внутри данного мувиклипа создаем 3 слоя. На первом создаём MovieClip крышку коробка. Называем её на как head. На втором слое рисуем 2 MovieClip-а по форме кнопок + и — , называем их на фрейме как btnPlus и btnMinus. На третьем слое рисуем прямоугольный MovieClip по всей форме нашего коробка, он будет служить контейнером для наших спичек. Назовём его на фрейме как conteiner. И оставляем на сцене.

Также рисуем картинку спички, делаем с неё MovieClip и удаляем со сцены.

Теперь напишем класс MatchBox наследующий наш класс Box. Это будет класс для нашего коробка и класс Match – спичка, наследуемый от класса MovieClip.
package{
        import flash.display.MovieClip;
        import flash.events.MouseEvent;

        public class MatchBox extends Box
        {
                public var btnPlus:MovieClip;
                public var btnMinus:MovieClip;
                public var conteiner:MovieClip;
                public var head:MovieClip;

                private var maxMatchCount:Number = 13;
                private var matchCount:Number = 0;
                private var matches:Array = new Array();

                public function MatchBox()
                {
                        head.addEventListener(MouseEvent.CLICK, OnHeadClick);
                }

                private function OnHeadClick(e:MouseEvent):void
                {
                        Close();
                        head.removeEventListener(MouseEvent.CLICK, OnHeadClick);
                        conteiner.addEventListener(MouseEvent.CLICK, OnConteinerClick);
                        btnMinus.addEventListener(MouseEvent.CLICK, OnBtnMinusClick);
                        btnPlus.addEventListener(MouseEvent.CLICK, OnBtnPlusClick);
                }

                private function OnConteinerClick(e:MouseEvent):void
                {
                        Open();
                        head.addEventListener(MouseEvent.CLICK, OnHeadClick);
                        conteiner.removeEventListener(MouseEvent.CLICK, OnConteinerClick);
                        btnMinus.removeEventListener(MouseEvent.CLICK, OnBtnMinusClick);
                        btnPlus.removeEventListener(MouseEvent.CLICK, OnBtnPlusClick);
                }

                private function OnBtnPlusClick(e:MouseEvent):void
                {
                        Add();
                }
                private function OnBtnMinusClick(e:MouseEvent):void
                {
                        Remove();
                }

                override public function Open():void
                {
                        head.y += head.height;
                        super.Open();
                }

                override public function Close():void
                {
                        head.y -= head.height;
                        super.Close();
                }

                public function Add():void
                {
                        if (matchCount <= maxMatchCount)
                        {
                                matchCount++;
                                var match:Match = new Match();
                                match.x = matchCount * 10;
                                match.y = 120;
                                conteiner.addChild(match);
                                matches.push(match);
                        }
                }

                public function Remove():void
                {
                        if (conteiner.numChildren > 1)
                        {
                                var obj:Object = conteiner;
                                conteiner.removeChild(matches.pop());
                                matchCount--;
                        }
                }
        }
}

package{
        import flash.display.MovieClip;

        public class Match extends MovieClip
        {

                public function Match()
                {}
        }
}

Теперь переходим в Flash IDE. Нажимаем Ctrl+L, в открывшемся окне находим клип нашего коробка и нажимаем на нём второй кнопкой, в открывшемся списке выбираем (в CS4) Properties (в CS3) Linkage. Ставим галочку на Export for ActionScript и в поле Class указываем класс MatchBox и нажимаем Ок. Аналогичное действие проводим с мувиком спички, указав класс Match.

Теперь нажимаем Ctrl+Shift+F12, в открывшемся окне выбираем вкладку Flash и на ней нажимаем кнопку Settings. В открывшемся окне убираем галочку с Avtomatically declare stage instance. Это позволит нам самостоятельно декларировать наши мувики в нашем же коде, что мы конечно уже сделали в классе MatchBox:
public var btnPlus:MovieClip;
public var btnMinus:MovieClip;
public var conteiner:MovieClip;
public var head:MovieClip;

Теперь тырцаем Ок, Ок. И пришол момент истенны: Ctrl+Enter. Смотрим что мы получили! Вот примерно и всё… Дерзайте!

Комментариев нет:

Отправить комментарий