ООП во FLASH 5

Настоящий Подкласс (Subclass)


Если бы подкласс существовал как класс информатики, мы бы могли уже сейчас создать подклассы Circle (Окружность) и Square (Квадрат), оба они содержали бы метод getArea (вычислить площадь) и каждый из них возвращал бы площадь, вычисленную абсолютно разными способами. На самом деле это изящная, но отчасти бесполезная демонстрация того, как OOП позволяет использовать один и тот же интерфейс для разных объектов. Важно другое: вы уже не видите "внутренних" калькуляций, а просто верите каждому, кто выдаёт правильный размер площади.

Это известно под термином "инкапсуляция" - заключение в непроницаемую оболочку чуждых среде элементов, или, проще говоря: "КАК ты это делаешь - твои проблемы, для меня главное, чтобы дело не стояло". Может, у вас уже был такой босс, если по этому поводу не особенно переживать, то работать вполне можно. Ведь для того, чтобы ставить такую задачу, "босс" и не должен быть программистом или графическим дизайнером, он тебе просто доверяет, исходя из твоего резюме и того опыта, что ты излагал ему при поступлении на работу. Разумеется, можно предположить, что в детском садике "босс" ни разу не рисовал цветными мелками, и, стало быть, не ему давать тебе советы по работе с цветом. С другой стороны, и руководство компанией, ежедневное и выматывающее, тоже не твоя область деятельности. Поэтому хорошее доброе отстранение поможет вам обоим не вдаваться глубоко в "чужие" сферы, не пересекаться в этих полях и не мешать друг другу. Разумеется, кто не выполняет работу, того увольняют. И не надо бояться применять эти стандарты поведения в классах, которые оздаёшь и используешь.

Идея инкапсуляции, или сокрытия неактуальной для пользователя информации, должна всегда быть у вас на уме при проектировании классов. Сообщать пользователю следует лишь то, что ему нужно знать, и ничего из того, что ему знать не нужно. Более того, пользователю совершенно незачем быть в курсе всех деталей работы класса (типа способа вычисления площади) перед его использованием. Всё, что ему нужно, уверенность в том, что значение площади возвращается, когда он вызывает метод getArea, независимо от типа геометрической фигуры.


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

Теперь мы, как и собирались, создадим два подкласса, Circle (круг) и Square (квадрат). Но что же делает их настолько отличными от Shape (фигуры) и друг от друга, что потребовались отдельные подклассы? Наиболее очевидный ответ: они по-разному выглядят. Верно, но на этом вся сила данного аргумента и исчерпывается. В конце концов, красный квадрат отличается от синего квадрата, а круглая маска похожа на всё, что находится под ней. Фактически, пока наша форма не похожа ни на что, классы Circle и Square, скорее всего, также обречены на "бесформенность". Ведь можно же представить такой класс Circle, который работает только с формулами. Хотя, скорее всего, вы вовсе ни в чём не уверены и вам кажется, что класс Circle, в принципе, заведомо обязан быть похож на круг. А что если мы заведём другой подкласс класса Shape, класс Polygon (многоугольник), например? Можно ли по изображению многоугольника судить о его идее? Как выглядит "многоугольник", ну-ка? Разумеется, многоугольник не обязан быть визуально "похожим" на что-то, он просто должен быть чётко выражен математическими терминами, находиться в пределах неких "математических ограничений". А уж компьютеры-то как это любят!

Итак, нужно задать "математические условия" для кругов и квадратов. Не волнуйтесь, ребята, это не сложнее задачи для пятиклашки! Всем известно, что круг задаётся через точку центра и радиус, а квадраты имеют длину и ширину. *Бабах!* С этим звуком реальность врезалась в нашу теорию. Неважно, программистом какой глубины мысли вы являетесь, но, вероятно, уже создавали во Flash как круг, так и квадрат. И знаете, что у них есть позиции x и y, а также ширина и высота. Если любите думать в терминах X, Y, W, H, посмотрите на панель "Info". Эти значения есть даже у кругов. Возникает вопрос, а где радиус? Отвечаем: во Flash'е всякие там "радиусы-катеты-гипотенузы" не работают. И вы это должны бы уже знать. Круги находятся, так же как и квадраты, в условных рамках (bounding box), просто они не заполняют эту рамку по самые углы. У кругов есть и ширина и высота, а свойства X и Y представляют собой характеристики левого верхнего угла ограничивающей их рамки (точнее - точки привязки этой рамки, а такая точка может находиться и не в левом верхнем углу, а, например, в центре - прим. переводчика).



Здесь возникает еще один аспект реальности: как Круги, так и Квадраты имеют ширину и высоту, унаследованные от класса геометрических фигур. Вспомните учебник пятого класса: эти величины должны быть одинаковыми и для круга, и для квадрата. Вот вам и прекрасный шанс для использования OO, переопределите метод setWidth и одновременно с шириной установится высота, и наоборот. Метод setSize может быть перекрыт и второй параметр будет проигнорирован. Будет логично предположить, что Квадрат и Круг могут наследовать типовые характеристики от Прямоугольника и Эллипса, являясь их частными случаями (квадрат это тот же прямоугольник, если помните). Что ж, чудесно. Теперь можно и выпить. Алкоголь избавит нас от подсознательного ощущения, что здесь что-то не так... Такое вот "мастерское" использование перекрытых методов кажется вполне достаточным. Но это только кажется. В методах скрывается гораздо большее.

А для чего, собственно, снова задавать подкласс фигуры? Мы хотим сделать небольшую программку, которая создаёт фигуры и позволяет перетаскивать их туда-сюда. Несомненно, нам хочется разных типов форм, но в качестве отправной точки круги и квадраты выглядят вполне логично. И снова, если присмотреться, а не являются ли для Flash круги и квадраты одним и тем же? То бишь мувиклипами с графическими данными внутри? Копнём глубже, так ли уж нам важно наличие радиуса у круга? Нужно ли нам вообще это знать? Ведь не для разрисовки же пиксел за пикселом, в самом деле?! Может, это нужно для hitTesting'а, но для этого есть кнопки, свойство hitTest или даже какой-нибудь dropTarget для красоты... Не будем выделять что-то одно, но есть ли что-нибудь ещё?

При появлении первых же сомнений правильнее всего возвращаться к истокам. "Ты человек, твоё предназначение на земле размножаться". Ну, может, предназначение человека и не только в этом... Для вас, как для разработчика классов, главная цель, выяснить - "что может (олуха) пользователя этих классов осчастливить". Для разработчика приложения (использующего классы), главное - "что надо сделать, чтобы осчастливить (законченного придурка) пользователя приложения". Для конечного пользователя главное - "потёр две дощечки друг о друга - добыл огонь". Сейчас мы "разработчики классов". Ну и чего же хочет пользователь этих классов? Есть хорошее упражнение, чтобы выяснить ответ, попробуйте представить, что задача решена и решена на "отлично" и напишите некий код, использующий это:



shape1 = new Polygon (); shape1.Location = new Point (168, 168); shape1.points = { new Point(0, 0), new Point(50, 30), new Point(30, 60) };

shape2 = new Circle (); shape2.Location = new Point(168, 168); shape2.Size = new Size (176, 56);

Итак, мы позволили пользователям нашего класса отвечать на поставленный вопрос. И даже если класс круга не делает ничего отличного от того, что делает класс квадрата (кроме отображения круглого мувиклипа), пользователь уже думает о них, как о разных вещах, так что отдельный класс всё-таки нужен. Конечно, наш горе-пользователь "представляет в уме" эллипс, называя его кругом (Глумливо так: "хе-хе"). Фактически, сразу после использования круга, он растянет его, чтобы превратить круг в эллипс! Да, перед нами дилемма. Использовать ли нам неправильные названия, пособничая тому, что пользователи классов называют эллипсы кругами, прямоугольники квадратами, а магнитофоны флейтами? Или (высокой цели ради), пойти трудной дорогой и использовать правильные термины, примирившись с тем, что пользователи проклинают нас каждый раз, когда используют "круг" в смысле "эллипс", да и просто когда не могут написать "эллипс" без ошибок? Разумеется, мы выберем "высокую цель". Ведь если мы назовем эллипс кругом, это введёт в заблуждение всех, кто до этого думал правильно, и они подумают, что более не смогут использовать "круг" как "эллипс". Кроме того, за правильное использование слова никого ещё не расстреляли. Да!.. и постарайтесь не давать название "Растрата" классу своих финансовых приложений.

А пока создадим-ка два подкласса Фигур: Эллипс и Прямоугольник. Потому, что это делается легко. Нам кажется, по крайней мере сейчас, что они не будут делать ничего, кроме создания формы и запоминания своих собственных имён (мы намеренно не касаемся пока работы с мувиклипами, как вы, наверное, заметили).

Ellipse = function(){} Object.extends(Shape, Ellipse); Rectangle = function(){} Object.extends(Shape, Rectangle);

Пока мы не начали заниматься многоугольниками, посмотрите, есть ли проблемы в вышеприведённом коде? Сам код выглядит хорошо (кроме ужасного синтаксиса flash 5, который использован для создания наследования!), но не звучит ли "Прямоугольник" слишком знакомо? Не задавали ли мы также для объекта все его xywh, используя прежде прямоугольник? Да, именно так мы и делали, или, по крайней мере, говорили об этом. Класс Rectangle уже есть, он совершенно не связан с новым классом. Это проблема, потому что один из них перезапишется поверх другого (_root.Rectangle и _root.Rectangle!). И слава Богу, что мы обнаружили эту проблему сейчас, а не после написания километров кода. Следующий раздел - о Пространствах имён и он касается (ну надо же!) именно этой проблемы. Какое совпадение!

<<

   ООП во Flash 5 ( II )    >>


Содержание раздела