![]() |
Глобальные переменные и ООП
Наверное глупый вопрос, но всё же.
Где правильно с точки зрения ООП хранить переменные, которые используются большинством классов, на протяжении всего приложения? Допустим есть класс, в котором содержится 10 других классов и 3 переменные, которые всем этим классам будут нужны: Код AS3:
класса MyClass может быть несколько, с разными значениями val1 - val3. Сейчас я не придумал ничего лучше, как передавать переменные в конструктор каждого класса, но это очень неудобно и замусоривает код. |
Непонятно, при чем здесь ООП и глобальные переменные.
Объясните, чего Вы хотите и что не получается. |
Я хочу понять, где правильно хранить переменные, которые по всем своим признакам выглядят как глобальные. Получаться то всё получается, но явно идеологически не выдержано передавать
классам глобальные переменные в конструкторах, чтобы те в свою очередь передавали их другим, вновь созданным. Должен быть какой-то более элегантный способ. |
Цитата:
Добавлено через 2 минуты Нет ничего элегантней умного и осведомленного директора, дающего четкие указания исполнительной и следящей за собой секретарше, не сующей нос в его дела, Вы не находите? Разве что умный, интеллигентный кавалер, наливающий даме шампанское? Если дама перед этим наклюкалась где-то на стороне глобально доступного пивка, элегантность ситуации несколько омрачается. |
Цитата:
Мой мозг, испорченный ассемблером, ищет ответ, что есть правильно, с точки зрения ООП. Добавлено через 13 минут Чтоб не быть голословным, приведу простой пример. Есть суперкласс, который создает экземпляр BitmapData и определяет его ширину и высоту. Код AS3:
работают с BitmapData. Рисуют в него, копируют и т.п. Им всем нужен доступ к width, height и bmp. Более того, в каждом из этих классов могут быть созданы другие классы, которое тоже захотят работать c этим битмапом. Вопрос: правильно ли будет всем создаваемым классам передавать эти переменные в конструкторе (или других методах). Или можно объявить их как статические, но тогда как быть, если экземпляров суперкласса будет два? |
Сама идеология классов в AS cодержит очевидный ответ на Ваш вопрос. Передавать правильно ( и чаще всего именно в конструкторе, ) но не все параметры, а лишь ссылку на объект родитель.
|
Цитата:
в комнате есть шкаф, в шкафу есть книга, в книге есть закладка и закладка будет знать про комнату? |
Нет, это не нормально как правило с точки зрения самой архитектуры. Взгляните под другим углом на решение своей задачи, возможно найдете более правильное решение построения архитектуры. Однако, в любом случае, если уж и передавать, то именно ссылку на объект, в котором содержатся все эти переменные, собственно, как и предложил Yahen.
|
ок, спасибо )
|
т.е. статика это плохо да? я не могу никак понять, одни пишут что это хорошо, другие наоборот... вот у меня например класс в котором хранятся переменные уровня в игре (ссылки на игрока, на контейнер для графики и т.д.) и менеджер событий клавиатуры все время через этот класс берет ссылку на игрока чтобы ему же изменить состояние, целесообразно ли переделывать (ну в смысле переменную в этом классе создать, чтобы он через чигири не лазил)?
П.С. просто не хочется чтобы мой код выглядел нубским =) |
Цитата:
|
Статика - это один для всех. Поэтому
Цитата:
|
ну у меня как-бы 1 игрок, 1 уровень и всего остального что там лежит тоже по одному, а экземпляров этого класса как-бы вообще нету... просто перенести переменные в контроллер уровня вообще не проблема, надо только понять имеет ли это смысл.. я подозреваю что имеет, но хочется точно узнать чтобы зря не делать лишних движений
Добавлено через 18 минут кстати по поводу менеджера клавиатуры, кто как это делает? у меня сейчас кейкоды нажатых кнопок хранятся в векторе<юинт>, но только что в голову пришла мысль что можно все хранить в одной переменной юинт и проверять побитово с помощью оператора &... |
Цитата:
|
Цитата:
Совершенно нормальная структура. Добавлено через 1 минуту Цитата:
|
Цитата:
|
Цитата:
Вот Вы приводите пример, и сразу хочется сказать – почему три, если передается битмапдата, то у нее уже есть ширина и высота, зачем передавать их отдельно. И так будет с каждым конкретным примером. Далее – если классы работают с битмапдатой, то как блин Вы можете им ее не передавать?))) Спроектировать классы так, что они будут запрашивать битмапдату у какого-то "глобального" класса-хранилища? Это смерть ООП. Это значит, что такой класс-попрошайка не сможет быть использован больше ни в одном проекте. Вы можете сделать электромобиль и заряжать его в своем гараже. Но если поедете на нем в другой город, Вам надо построить там зарядную станцию. То есть в другом проекте для использования этого побочного классика Вам придется разворачивать глобальную структуру поддержки на верхнем уровне. Он не самодостаточен как исполнитель. Это такая секретарша заведующего отделом, которая по любому вопросу звонит главе компании. И это семечки – глава компании при этом должен еще и знать ответы на ее местячковые вопросы. ООП без иерархии это свалка. Это когда "контроллер клавиатуры" почему-то управляет персонажем, получивший новые настройки стилей xml-лоадер рассылает события сотням детей-GUI, и те толпами ломятся к нему за своими каскадами стилей, вместо того чтобы просто спустить каскад вниз и каждому выдать конкретное указание... да мало ли каких странностей не бывает. Для того и придумано ООП, чтобы делать "по правилам", по единым и понятным правилам, не изобретая велосипедов и не создавая хитрых креативов, в которых придется разбираться потом самому, коллегам, компилятору и плееру. Но это уже каждый решает сам – воспользоваться чужим опытом и шаблонами, или положиться на свой гений и скреативить новое невиданное архитектурное решение.. Которое чаще всего оказывается решением на один раз и без возможности развернуться корпусом. |
Так вот и хочется всё сделать максимально универсально и масштабируемо. Чтоб модель не зависела от представления, а контроллер не оперировал данными.
Цитата:
Пример про битмапдату - просто пример. Добавлено через 4 минуты допустим та история с комнатой, в которой шкаф, в котором книга. книга может испортиться, если влажность воздуха в комнате повысится. но я не хочу передавать в шкаф ссылку на комнату, а из шкафа в книгу ссылку на комнату, чтоб книга знала, какая в комнате влажность. я могу сгенерить событие "Влажность изменилась" и передать в аргументе события эту самую влажность. Книга на неё отреагирует и сгниёт :) |
Это как раз самый характерный пример супермена.
"Книга отреагирует на влажность в комнате" означает, что область ответственности книги простирается далеко за ее пределы. И что книга САМА решает, как ей реагировать на внешние изменения. Это не только нарушает иерархию, это как раз тянет за собой строительство станций обслуживания книги, которые должны будут держать книгу в курсе того, что происходит вовне, а книга должна обладать достаточно развитой логикой, чтобы реагировать на разные внешние факторы. Нетрудно догадаться, что этих факторов почти столько же, сколько объектов в программе. Вот аналогия. Мама звонит дочке, которая в школе, и сообщает ей: "У нас кончилась колбаса". FamilyEvent.HUNGER_BEGIN, data="sausage". Это ваша ситуация с книгой, ваше решение отношений. Родитель посылает ребенку событие. Теперь дочка должна как-то среагировать. У нее должно быть достаточно логики, чтобы пойти после школы в магазин, снять с карточки денег, выбрать сорт и количество колбасы, купить ее и доставить домой. (хотя в вашем варианте "сгнить" дочка скорее должна "съесть" колбасу, а не "доставить домой". Тут есть определенная разница)) но это лишь детали). Кроме того, устройство детского организма не гарантирует, что дочка вообще как-то будет реагировать на событие. Во всяком случае, посылающая сообщение мама всего лишь посылает сообщение. Это просто dispatchEvent, и он ни к чему не обязывает получателей. Как эта ситуация должна была бы разыгрываться при иерархических отношениях? Мама звонит дочке и говорит "после школы зайди в "Веселую Сардельку" и купи полкило Брауншвейгской". gotoAndBuy("Веселая Сарделька", "Брауншвейгская", .5); И может заодно подписаться на событие BuyErrorEvent.STOCK_OUT, на случай если дочка позвонит из магазина и скажет, что Брауншвейгская закончилась. Потому что решать, какую колбасу купить взамен, должна мама, а не дочка. Итак, от старших вниз идут приказы – прямые вызовы конкретных гарантированных методов детей, а от детей вверх – события и ошибки, то есть информация "с мест". Когда Вы возьмете этот класс дочки и перенесете в другой проект, вы будете точно знать, что она не спустит все ваши денежки на пирожные и не будет пытаться вызывать методы ваших старших классов, а в самом худшем случае будет звонить в пустоту (ну технически на самом деле не будет, если не будет подписчиков, но мы сейчас о логике разговариваем)). Что класс будет делать ровно то, что ему прикажут более ответственные товарищи. В вашем случае комната прикажет шкафу "увлажнись(120)", а шкаф прикажет всем своим обитателям "увлажнись(120-10)". Здесь книга может проверить свой лимит и выбросить сообщение "я же так сгнию!?". А вот понизит ли Комната влажность из-за сообщения от младшего члена – это логика Комнаты, и эта логика вправе проигнорировать событие от Книги, но отреагировать на событие "я же так сгнию!" от Шкафа, как более ценной сущности, например))) Мало того, Книга не должна сама гнить. Её кто-то создал, и только создатель знает, зачем. Книга не должна сама себя разрушать, удалять и т.п. Она – инструмент в какой-то большой игре, знать которую ей не надо. Только создатель может распоряжаться ее судьбой. Книга же должна отвечать только за своих подчиненных – приказывать страницам листаться и т.п. |
Пример с книгой очень плохой. Книга именно сама и гниет. Шкаф тут вообще не причем.
Если брать примеры из жизни, то ООП работать не будет. Именно потому, что все объекты сами по себе. Понятие родитель-ребенок весьма условны. Вот книга на полке и она подчиняется полке только пока действует сила тяжести и только в направлении полки и стенки. Но стоит ее поднять и связи уже не будет. По сути, единственный пример из жизни, которые выстраивает иерархию, это армия. Есть командир, и есть подчиненный. Приказ приходит сверху и уходит вниз. И чем ниже чин, тем меньше его свобода действий. |
:) Шкаф "при чем" в случае влажности, он таки предохраняет книгу, и может даже иметь влагозащитные свойства, корректирующие влажность внутри. Если уж в программе реализована система влажности, влияющая на параметры объектов. Согласен, конечно, пример не самый наглядный. Но и в программе, особенно на стадии проектирования, не все отношения так наглядны, как в армии. Потому и надо абстрагироваться и искать общий принцип, и потом уже подчинять ему детали реализации.
|
Код AS3:
Цитата:
Речь пойдёт о вьювере (представлении в терминологии MVC), который должен нарисовать модель - сегмент игры, содержащий данные и игровую логику. Допусти пользователь куда то тыкнул и вылезло окошко. Модель генерит событие (но щас речь не об этом) и вьювер должен как то это окно нарисовать. Есть суперкласс view, в котором помимо прочего есть класс GUI, в котором есть класс window, в котором, в свою очередь есть класс AttackConfirmDialog, у которого есть метод Show. Заметьте - эта цепочка может иметь сколь угодное количество звеньев. Если следовать вашей логике, в ответ на изменение модели, я должен буду сделать что-то типа view.GUI.window.attackConfirmDialog.show() Но это утопия. По скольку диалогов может быть сотня, а событий, которые генерит модель, крича о том, что она изменилась и того больше. И тогда класс view у меня превращается в класс-бога, который должен знать всё и про всех. Пользователь подвинул мышь - view.GUI.cursor.setPos(), нажал меню - view.GUI.window.saveMenuDialog.show() и т.п. Вот это и есть смерть ООП. Я поступил следующим образом - реализовал в представлении (вьювере )диспетчер сообщений и поместил его экземпляр в класс view. Теперь, когда модель говорит, что надо нарисовать окошко или контроллер говорит, что пользователь подвинул мышь, ядро движка обращается к диспетчеру сообщений view и тот уже генерит событие типа ON_WINDOW_SHOW или ON_MOUSE_MOVE, на которое подписаны соответственно классы window и cursor. Всё элегантно и просто. И незачем порождать цепочку вызовов. Корневому классу view незачем знать, что где-то есть метод диалога show и уж тем более незачем знать, какого из сотни диалогов именно. Максимум, что он может знать - это то, что можно дать команду GUI нарисовать окно, а тот уже сам разберётся. Но в моём случае, с диспетчером сообщений и это не обязательно. |
Цитата:
У меня страницы книги листала книга, а не город, в котором дом, в котором комната, в которой шкаф, в котором книга. Никогда не думал, что в MVC место отображения курсора или даже диалоговое окно как-то зависит от модели. Мне всегда казалось, что с этим прекрасно справляется вью, а модель интересует разве что результат выбора пользователя. Но я не эксперт в MVC. Да и традиционный холивар о том, кто как готовит MVC меня сейчас совсем не интересует. Жаль только, что не удается донести до Вас мысль о прелестях иерархии и разделения ответственности. |
Цитата:
Например военный советник предупреждает о нападении. Но речь вовсе не об этом и не о MVC Цитата:
Если внимательно почитать первую страницу, вопрос был в том - как правильно "спускать" переменные вниз. Не хотелось передавать ни кучу аргументов в конструкторы, ни уж тем более ссылку на верхний класс. Добавлено через 13 минут конкретно в моём примере Цитата:
а так же методу view.render.render() следовательно он должен лежать где то наверху, на уровне view. и я его должен как то спустить до уровня view.GUI.window.attackConfirmDialog.show() но он (битмапдата) нафиг не нужен не GUI, не window, не кучи других классов. Вот как быть? Добавлено через 31 минуту т.е. у меня либо ошибка проектирования архитектуры, и класс attackConfirmDialog не должен знать о переменной верхнего уровня BitmapData и следовательно не должен обладать методом Show(). И тогда получается окно не должно само себя рисовать на глобальном холсте, а это должен делать какой то "глобальный" рендер, перебирая все открытые окна и рисуя их. Либо я сдаюсь тогда ) |
Не знаю, как устроен view.render.render(), как он рендерит attackConfirmDialog, не имея на него ссылки, поэтому ответ опять получится расплывчатым.
В целом Вам уже ответили. Если надо спускать сверху вниз, то надо спускать, какую бы нелогичность Вы в этом не увидели. Никто не заставляет промежуточные классы хранить ссылку, достаточно передать ее по цепочке методов. Подчеркиваю: если есть необходимость. Если битмапа нужна только attackConfirmDialog, то логично наверно там ее и хранить. Если она нужна разным производным window, то место ей в window. Да Вы и сами это прекрасно понимаете. Загвоздка в рендере, который видимо не может спросить часть attackConfirmDialog у самого attackConfirmDialog. Цитата:
Если нужны для какого-то паблик метода, они могут быть переданы методу. Без передачи ссылки на верхний класс Вы не осуществите свою схему с подпиской мелкого на событие от старшего (и слава богу) и тем более отдельное Хранилище Всего (разве что статическое с импортом класса Хранилища во всех мелких – надеюсь об этом речи вообще не идет). Вобщем я хочу еще раз сказать, и надеюсь последний.. Весь кажущийся ужас передачи Вы придумываете себе сами, рассматривая кошерную передачу на фоне некошерной архитектуры. Когда иерархия выстроена правильно, никаких нелогичных передач не остается. Если симметрия ответственностей нарушена, добиться нужных связей можно только введением нелогичных членов и обходных путей передачи - хранилищ, менеджеров, провайдеров, прочих отростков. Надо стремиться к целостности и четкому разделению ответственности. Поэтому комментировать конкретные примеры/фрагменты бессмысленно. Будем бесконечно ходить по кругу. |
Цитата:
а attackConfirmDialog рисует в эту битмапдату диалоговое окно. По этому ссылка одному на другого не нужна - у них общая битмапдата. Сейчас саму битмапдату я положил в отдельный класс-хранилище и спускаю обоим в конструкторах. Но интуитивно понимаю, что это неправильно. |
Просто наверное лучше наоборот чтобы у объекта был метод getBitmapData, ну или там свойство BitmapData, класс родитель пробегался бы по всем объектам и рендерил бы каждый в "стэйдж" на который имеет ссылку. Или нет?
|
Цитата:
Добавлено через 1 минуту hvostoblud, ага. Это называется BitmapData#draw(). |
тогда получается что:
1. У каждого объекта, который нужно нарисовать, должна быть своя, локальная битмапдата, в которую он рисует, а рендер, пробегая по ним, уже рисует их на глобальный холст. Ну это я ещё могу понять, в принципе это логично. 2. Рендер должен знать обо всех объектах, которые нужно пробежать. Вот с этим ка быть? Допустим, волею судеб у мня все объекты интерфейса лежат в view.GUI и там распиханы по своим классам. Т.е. мне нужно делать массив ссылок на объекты, которые должны быть прорисованы или как? Как рендер узнает о них всех? |
Цитата:
|
Код AS3:
Другое дело если по соображениям оптимизации построено как-то так, что при наезде мыши на кнопочку перерисовываться в большой битмапдате должна только кнопочка. Но я и тут не вижу никаких проблем. |
Код AS3:
с технической реализацией проблем нет. Добавлено через 6 минут т.е. что лучше - дать каждому элементу интерфейса общую битмапдату, пусть туда рисуют (у меня сейчас так ) или городить список элементов и проходить их в цикле. И если да, то где этот список держать. |
Во вью?
|
видимо.
вопщем буду пробовать ) |
| Часовой пояс GMT +4, время: 19:48. |
Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.