Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Как правильно расширить класс? (http://www.flasher.ru/forum/showthread.php?t=215556)

djken 15.05.2018 01:26

Как правильно расширить класс?
 
Привет всем.
Подскажите пожалуйста как сделать правильно.
Есть класс ImageBox - некий контейнер, которому можно задавать размеры, в себя умеет загружать картинку и пустое место заполнять выбранным фоном.

От этого класса будут производные классы. Например, класс ImageBoxExt - делает все тоже самое что и ImageBox, но еще при наведении должны внутри появляться кнопка корзины и чекбокс.

http://www.flasher.ru/forum/attachme...chmentid=33228


Наследоваться от ImageBox?
Код AS3:

public class ImageBoxExt extends ImageBox

Проблема: в конструкторе после super добавляю кнопку корзины и чекбокс. Но после того как загружается основная картинка, она закрывает все новосозданные кнопки ) Следовательно, в этом классе нужно перекрывать родительские методы - а это неправильно.

Либо просто создавать класс, наследуясь от Sprite и внутри создавать экземпляр класса ImageBox?
Код AS3:

public class ImageBoxExt extends Sprite
{
  private var _ib:ImageBox;
...
  _ib = new ImageBox(...);
  addChild(_ib);
  // и тут добавлять новые кнопки
...
}


undefined 15.05.2018 02:58

Цитата:

Следовательно, в этом классе нужно перекрывать родительские методы - а это неправильно.
Почему?

djken 15.05.2018 08:38

Undefined, почему нужно перекрывать? Или почему это неправильно?

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

А неправильно, т.к. это нарушение инкапсуляции..

caseyryan 15.05.2018 10:06

Цитата:

Следовательно, в этом классе нужно перекрывать родительские методы - а это неправильно.
Неправильно? Это один из принципов ООП, так сказать, один из трёх китов)
Цитата:

А неправильно, т.к. это нарушение инкапсуляции..
Нет, это неправильная архитектура изначально.

Если изначально планируется, что в базовом классе грузится какая-то картинка, а объекты дочерних классов должны знать об этом, то и архитектуру родительского класса надо делать сразу с учетом этого.
Если нужно дождаться загрузки картинки, то можно обработчику сделать модификатор protected и в дочернем классе просто его перезаписать
Код AS3:

// в базовом классе
protected function onImageLoaded(e:Event):void {
      ... добавляется картинка
}
// в наследнике
override protected function onImageLoaded(e:Event):void {
    super.onImageLoaded(e);
    .. тут добавляешь свои чекбоксы
}

Я бы вообще добавление чекбоксов вынес из конструктора в отдельный метод, и просто вызывал его после загрузки картинки
Код AS3:

override protected function onImageLoaded(e:Event):void {
    super.onImageLoaded(e);
    addCheckboxesAndOtherCrap();
}

как-то так

djken 15.05.2018 13:55

caseyryan, спасибо большое!
Для личного пользования всегда обходился только private и public. Но в данном случае не хотелось использовать public у родительского метода для перезаписи в наследнике, т.к. onImageLoaded не относится к интерфейсу данного класса.

Теперь буду знать про protected )

Цитата:

то и архитектуру родительского класса надо делать сразу с учетом этого
Но все же - какая в данном случае архитектура была бы правильная? Или имелось ввиду использование protected?

caseyryan 15.05.2018 14:19

Цитата:

Но все же - какая в данном случае архитектура была бы правильная? Или имелось ввиду использование protected?
Правильно в данном случае - иметь возможность уведомить наследников о загрузке картинки.
Цитата:

Теперь буду знать про protected )
protected это всего лишь один из 4 штатных модификаторов доступа, означает доступ для наследников. Есть еще internal для свойств доступных внутри пакета. Можно и свое пространство имен объявить, но тут это избыточно

djken 15.05.2018 14:37

А, ну у меня в родительском методе вконце стоит диспатч события после загрузки картинки.
Код AS3:

dispatchEvent(new Event('COMPLETE'));

Получается я могу его так же в наследнике слушать.. я что-то не додумался сразу до этого )
Код AS3:

super.addEventListener('COMPLETE', onCompleteHandler);
...
 
private function onComplete(e:Event):void
{
        var _cb:CheckBox = new CheckBox();
        addChild(_cb);
}

Значит можно без override обойтись )
Пасиб! )

GBee 15.05.2018 14:47

Так может картинку на 0 уровень всегда пихать после загрузки?

undefined 15.05.2018 17:26

Цитата:

Значит можно без override обойтись
Тебе явно кто-то вбил в голову,что override - это плохо.Это не так.

caseyryan 15.05.2018 17:28

Цитата:

Сообщение от djken (Сообщение 1205242)
А, ну у меня в родительском методе вконце стоит диспатч события после загрузки картинки.
Код AS3:

dispatchEvent(new Event('COMPLETE'));

Получается я могу его так же в наследнике слушать.. я что-то не додумался сразу до этого )
Код AS3:

super.addEventListener('COMPLETE', onCompleteHandler);
...
 
private function onComplete(e:Event):void
{
        var _cb:CheckBox = new CheckBox();
        addChild(_cb);
}

Значит можно без override обойтись )
Пасиб! )

Ну, во-первых писать типы событий хардкодом типа "COMPLETE" - это моветон. Можно легко опечататься и потом думать и гадать, почему не работает. Всегда используй для этого константы.
Во-вторых, для события complete есть штатная константа Event.COMPLETE, даже свою объявлять не надо
Ну и в третьих - отправка события - это всегда более ресурсоемкий способ, чем вызов метода. У тетя там и так событие приходит при загрузке картинки. Создавать еще одно - это оверхэд. Оверрайд в данном случае более правильный подход. Не нужно ни слушателей дополнительных ни событий и всё по ООПшному
А если событие все-таки нужно для чего-то ещё, то ты можешь не создавать новое, а просто передиспатчить то же
Код AS3:

dispatchEvent(e);


djken 15.05.2018 18:17

Цитата:

Ну, во-первых писать типы событий хардкодом типа "COMPLETE" - это моветон.
Согласен. Привык так по-быстрому писать.. Надо будет на досуге разобраться как все же правильно. Константу объявлять в этом же классе или лучше создавать отдельный класс под события?

Цитата:

для события complete есть штатная константа Event.COMPLETE, даже свою объявлять не надо
Event.COMPLETE конечно знаю - использую, когда подписываюсь. Но не знал, что его же можно использовать и в кастомных.. Т.е. как я понял ты говоришь об этом - вместо
Код AS3:

dispatchEvent(new Event('COMPLETE'));

писать
Код AS3:

dispatchEvent(Event.COMPLETE);

А еще лучше перенаправлять
Код AS3:

dispatchEvent(e);

Значит оставлю override, раз это лучше. Буду знать. Снкс!

caseyryan 15.05.2018 20:54

Цитата:

Event.COMPLETE конечно знаю - использую, когда подписываюсь. Но не знал, что его же можно использовать и в кастомных.. Т.е. как я понял ты говоришь об этом - вместо
Код AS3:

dispatchEvent(new Event('COMPLETE'));

писать
Код AS3:

dispatchEvent(Event.COMPLETE);


Нет. Event.COMPLETE - это всего лишь строка. Это не событие. Она лишь указывается для задания типа событию. Поэтому писать надо так:
Код AS3:

dispatchEvent(new Event(Event.COMPLETE));


djken 16.05.2018 01:50

caseyryan, огромнейшее спасибо за помощь!

djken 24.05.2018 11:53

Наконец-то сел почитать, чтобы разобраться с Событиями досканально - многое прояснил для себя.
Но есть момент, в котором не знаю как правильно поступить: как раз столкнулся на моем примере, когда по совету от caseyryan стал не создавать свое новое событие, а передиспатчивать существующее - dispatchEvent(e).

Мне нужно ловить событие клика по спрайту _box, который внутри класса ImageBox.

Код AS3:

public class ImageBox extends Sprite
{
    private var _box:Sprite; // в этом спрайте рисуется бэкграунд и грузится картинка
    private var _cb:CheckBox; // чекбокс
 
    public function ImageBox()
    {
          _box = new Sprite();
          _box.addEventListener(MouseEvent.CLICK, onClickHandler);
          addChild(_box);
 
          _cb = new CheckBox();
          addChild(_cb);
    }
 
    private function onClickHandler(e:MouseEvent):void
    {
          dispatchEvent(e);
    }
}

Код AS3:

_imageBox = new ImageBox();
_imageBox.addEventListener(MouseEvent.CLICK, onClick);
addChild(_imageBox);
 
private function onClick(e:MouseEvent):void
{
    trace(e.target); // выводит [object ImageBox] и [object Sprite]
}

В итоге обработчик onClick срабатывает два раза.
Как я понимаю:
ImageBox имеет событие click, унаследовав от Sprite. И _box тоже. Подписавшись на событие click у _imageBox - из-за цепочки событий в иерархии отображаемых объектов на фазе захвата происходит диспатч сначала у предка [object ImageBox], а потом и у получателя [object Sprite].
Верно ли понимаю?

Вопрос: мне не нужно чтобы onClick срабатывал дважды, т.к. в нем будут производиться какие-либо действия дважды.. Вижу два выхода: 1) в обработчике проверять от кого пришло событие и только после этого выполнять дальнейшие действия. 2) либо все таки в onClickHandler диспатчить свое событие.

Подскажите пожалуйста.

undefined 24.05.2018 15:33

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

Wolsh 24.05.2018 17:16

От задачи же зависит..
Если вам надо событие клика по КАРТИНКЕ, но к самой картинке доступа нет и добавить ей слушатель нельзя, то придется диспатчить от имени контейнера и тогда да, лучше использовать кастомное событие. То есть обычный Event, но со "своим" строковым типом, который можно объявить константой в классе ImageBox, пусть будет ImageBox.CLICK_IMAGE.
Код AS3:

public class ImageBox extends Sprite
{
        static public const CLICK_IMAGE:String = "clickImage";
        private var _box:Sprite; // в этом спрайте рисуется бэкграунд и грузится картинка
        private var _cb:CheckBox; // чекбокс
 
        public function ImageBox()
        {
                _box = new Sprite();
                _box.addEventListener(MouseEvent.CLICK, onClickHandler);
                addChild(_box);
        }
 
        private function onClickHandler(e:MouseEvent):void
        {
                dispatchEvent(new Event(CLICK_IMAGE));
        }
}

Код AS3:

_imageBox = new ImageBox();
_imageBox.addEventListener(ImageBox.CLICK_IMAGE, onClickImage);
addChild(_imageBox);
 
private function onClickImage(e:Event):void
{
        trace(e.target); // выводит [object ImageBox]
}


djken 24.05.2018 17:46

Цитата:

Если вам надо событие клика по КАРТИНКЕ, но к самой картинке доступа нет и добавить ей слушатель нельзя
Wolsh, т.е. правильнее _box сделать публичной, чтобы можно было подписываться напрямую?
Код AS3:

_imageBox._box.addEventListener(MouseEvent.CLICK, onClick)

вернее через акцессор
Код AS3:

public function get box():Sprite
{
    return _box;
}

Код AS3:

_imageBox.box.addEventListener(MouseEvent.CLICK, onClick);

или все таки диспатчить свое событие clickImage?


undefined, попробовал stopImmediatePropogation, работает. Но не совсем понимаю, что значит dispatchEvent диспатчит клон?
В справке Adobe ничего не нашел про stopImmediatePropogation. Упоминается вскользь...
Цитата:

Прослушиватель события может изменить это поведение, вызвав метод preventDefault(). Также можно сделать текущий прослушиватель событий последним для обработки события путем вызова метода stopPropogation() или stopImmediatePropogation().

undefined 24.05.2018 18:00

Цитата:

Но не совсем понимаю, что значит dispatchEvent диспатчит клон?
Это значит,что когда ты пишешь
Код AS3:

dispatchEvent(e);

Рассылается не само событие e,а то,что вернет e.clone()(клон события).А само событие e продолжит свое путешествие по дисплейлисту,поэтому у тебя лисенер и срабатывал 2 раза т.к. ловил оригинальный ивент и клон.

djken 24.05.2018 18:05

undefined, теперь понял! Огромное спасибо за разъяснение!

Wolsh 24.05.2018 20:46

Цитата:

В справке Adobe ничего не нашел про stopImmediatePropogation. Упоминается вскользь...
Это методы класса Event, там и надо искать))
Цитата:

Wolsh, т.е. правильнее _box сделать публичной, чтобы можно было подписываться напрямую?
А он тебе нужен еще для чего-то "снаружи" ImageBoxa? У тебя возникала когда-нибудь неразбериха при подписке на Лоадер, что нужно лезть в какой-то Loader.loaderInfo? Меня это всегда раздражало. Тебе ведь, положа руку на сердце, нафиг там не нужен MouseEvent со всеми его 100500 данными на борту, где только координаты мышки в момент клика представлены аж шестью свойствами, а еще данные о зажатых/незажатых клавишах альт и шифт, и тому подобные подробности? Если тебя интересует только факт клика по картинке, то я бы только его и диспатчил. Тем более, что этот ImageBox, как я понимаю, будет и другие события посылать — о выборе в чекбоксе например, что-то еще; он часть системы, модуль, со своим поведением и характером, а картинка просто картинка, нет смысла ее публиковать на все приложение, она не выполняет функций — не имеет поведения, даже своего класса не имеет, и знать о ней никому не надо. Не ради клика точно.

djken 24.05.2018 22:12

Wolsh, понял ) Благодарю за исчерпывающий ответ!


Часовой пояс GMT +4, время: 20:22.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.