Форум 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=207274)

darkthor 05.04.2014 15:37

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

Вот к примеру у меня есть игра. В ней есть главный класс Game, класс с картой местности Map и класс Gui. Пример беру абстрактный (не из своего реального кода), просто чтобы смысл вопроса был понятен.
Код AS3:

public class Game extends Sprite 
{
    public static var instans:Game;
    public var map:Map;
    public var gui:Gui;
 
    public function Game(stage:Stage)
    {
        Game.instans = this;
 
        this.map = new Map();
        this.gui = new Gui();
    }
}

И вот например игрок нажал на кнопку в gui и нужно на карте показать какой-то объект. Самый простой способ это сделать это поставить в классе Gui такой вызов:
Код AS3:

Game.map.showObject();

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

Пока не придумал как сделать чтобы было и правильно и не сильно запутано. Есть только пара вариантов, в которых я не уверен:
1. Для таких случаев делать ф-ию в Game, в которой проверяется есть ли сейчас что-то в this.map и тогда уже вызывать this.map.showObject();
2. Повесить кастомное событие на this.gui. В Gui когда надо вызывать это событие, которое Game примет и уже будет с ним делать что надо.

Пока склоняюсь к версии с событиями. Но не уверен. Надеюсь, что опытные флешеры подскажут.

Wolsh 05.04.2014 17:07

Не вижу противоречия "либо первое, либо второе", это как бы и не разные варианты одного и того же, а просто две разных фазы.
GUI посылает событие — ну, нормальное решение. Вместо него возможен только вариант "GUI напрямую вызывает функцию в Game", что конечно же и являлось бы характерным примером "жесткой связности", которую следует избегать. Вот насчет "кастомное событие" я не всегда согласен, ибо кастомное означает, что его должны знать, то есть импортировать в себя, оба класса, что так же создает связность и сложности с переносом в другой проект. Но это не так категорично плохо, как прямой вызов методов.
У Вас Гейм является родителем для карты и интерфейса, он должен заниматься основной логикой. От ненужной логики его стоит разгружать. Упомянутый метод, который что-то там ищет в карте, не должен быть в Гейм, как мне кажется. Это ответственность Карты — она знает своих деток и она должна их считать, а не барневарн. Всегда надо проверять себя так: "если завтра я захочу перенести этот класс в другой похожий проект, придется ли мне писать 100500 строк для его интеграции и для его обслуживания в новом проекте?"

darkthor 05.04.2014 17:56

Цитата:

GUI посылает событие — ну, нормальное решение.
Не совсем понял что именно имеется в виду. Ведь чтобы передать что-то в Game нужно обратится к нему через Game.instans.blabla();

Wolsh 05.04.2014 18:11

Так вот чтобы дети не обращались к родителям, их и учат посылать события.
Это в частности удаляет связность, ведь родитель не ОБЯЗАН обработать событие; он даже может на него не подписываться, это его личное дело. Вы же не подписываетесь на ВСЕ события, которые умеет генерить Спрайт)) Спрайт и ваши кастомные классы — яркий пример хорошего разделения, абстрагирования классов. Спрайт не пытается вызывать какие-то методы у ваших классов? Именно поэтому Вы можете лепить из спрайтов любой проект. Он переносимый. Потому что ни от кого ничего не требует для своего комфорта и информирует всех, кому это интересно, о своих событиях.

Akopalipsis 05.04.2014 19:02

Цитата:

Не совсем понял что именно имеется в виду. Ведь чтобы передать что-то в Game нужно обратится к нему через Game.instans.blabla();
Так не нужно, Вы же и так создали экземпляры гуи в гейме.Теперь все что нужно, это подписать на событие эти самые гуи и когда пользователь нажмет на кнопку, то гейм поймает событие и примет решение, что нужно сейчас показать. И раз Вы дошли до момента, когда при написании приложений, в голове эхом отдаются слова - логика и правильно, то можете почитать книги, которые указаны в моей подписи.
Они очень интересные, но если честно, после прочтения, хочется читать ещё, но уже нечего.

И пользуясь случаем, немного оффтопа - если кто-то видел пример паттерна репозиторий на as3, дайте ссылку.

darkthor 07.04.2014 00:30

На счет переносимости классов я понял. Очень доходчиво объяснили, за это спасибо. Я в общем-то по этому и задумался над этой всей темой. Но с событиями не понял один момент. Почему нежелательно использование кастомных событий. Под ними я имею в виду свои классы событий которые наследуются от Event. Вот пример: Пользователь нажал на кнопку. Кнопка была создана в классе Gui. Получается что обработчик события click находится в Gui. Теперь нужно сообщить остальной части программы что случилось. Для этого есть к примеру класс GuiEvent. В Gui делаю dispatchEvent(GuiEvent.ButtonUsed).
В Game вешаю слушатель этого события, чтобы в нужный момент вызвать что-то в классе Map.
Вот и получается что никакие классы не работают со своими родителями. Этот вариант мне симпатичен. Но наверное тогда получится много кода в Game. По событию на каждое действие в Gui которое влечет за собой действие в Map. Хотя может это и правильно.

Добавлено через 6 минут
За литературу спасибо тоже )

Добавлено через 35 минут
Хотелось бы еще узнать ваше мнение на счет одного способа взаимодействия классов. Например у меня есть класс который представляет из себя окно. В окне можно выбрать предмет из списка. Я могу в конструктор этого класса-окна передать функцию callBack. И когда объект будет выбран - будет вызвана эта функция.
Тот же метод я хочу применить в классе загрузщика файлов. Будет ли это хорошим кодом?

Wolsh 07.04.2014 01:08

Цитата:

Но с событиями не понял один момент. Почему нежелательно использование кастомных событий.
Я уже сказал, что это на самом деле не так важно и нет особого смысла вылизывать все до перфекшена. Неидеальность здесь в том, что и класс Gui и класс Game зависят от класса GuiEvent. Соответственно, если Вам захочется перенести класс Gui в другой проект, этот проект должен так же знать GuiEvent, фернштейн? И наоборот, чтобы заменить класс Gui на другой, Вы должны обеспечить чтобы этот другой посылал события именно класса GuiEvent. Не то чтобы это смертельно, ведь не так уж часто переносят такие классы по проектам. Тем не менее, это связность через "третье лицо". Сейчас понятней?

Akopalipsis 07.04.2014 01:08

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

package 
{
        public class EventOfComponentsOut
        {
                public static const EXECUTE:String = "execute";
        }
}

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

// подписываемся на событие используя родное событие Event
addEventListener(EventOfComponentsOut.EXECUTE, executeHandler);
 
// посылаем событие используя родное событие Event
dispatchEvent(new Event(EventOfComponentsOut.EXECUTE));
 
private function executeHandler(event:Event):void
{
 
}

Но тут есть одно но, если вам нужно передать объект вместе с событием, то придется переопределять класс Event и добавлять туда нужный параметр. Но если Вам нужно передать вместе с событием строку, например значение какой-то константы или ещё чего-то, то у флеша для этого есть тоже родное событие - DataEvent,
которое нужно применять так, как я показал выше, только вместо Event писать DataEvent.
И спасибо Wolsh

Wolsh 07.04.2014 01:18

Попросту, если есть возможность использовать обычный Event и DataEvent, нет смысла городить наследников. В большинстве случаев Вам не надо передавать кучу данных с событием. В случае с Gui тем более — никаких координат, всегда можно обойтись просто строковым типом объекта класса Event. Даже в случае с данными, например уровень громкости и т.п., можно использовать стандартный DataEvent, хотя там и некрасиво получается перевод числа в строку и обратно. Зато эти классы стандартные, их не получится забыть, потерять или неожиданно изменить.

darkthor 07.04.2014 01:34

Все понятно. Всем спасибо :)


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

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