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

Appleman 08.08.2018 17:39

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

Имеем в классе свойство и простой геттер (без каких-либо преобразований) для него:

Код AS3:

public class SomeClass
{
private var _someVar: Number = 0;
 
public function get someVar() : Number {return _someVar}
}

У нас внутри этого класса есть метод, который использует в своих расчётах значение свойства _someVar. Как обычно принято обращаться за значением: this.someVar или _someVar?

Tails 08.08.2018 18:09

Второе.

СлаваRa 08.08.2018 18:22

Код:

Второе.
обязательно, если вы хотите сломать полиморфизм :)

Wormhole 08.08.2018 21:16

В зависимости от того, что именно требуется. Ведь если свойство и псевдо-свойство (симулируемое геттером) ничем не отличаются, зачем геттер?

Wolsh 08.08.2018 22:07

Цитата:

зачем геттер?
Затем что он паблик, то есть свойство здесь read-only.

Разные отклонения могут возникнуть при наследовании, например у наследников геттер станет возвращать преобразованное значение (не в сантиметрах а в дюймах например). Заранее трудно предусмотреть))

Appleman 08.08.2018 22:55

Цитата:

Сообщение от Wolsh (Сообщение 1205846)
Затем что он паблик, то есть свойство здесь read-only.

Да, но это не главное. :) Просто сеттер не относится к сути вопроса, поэтому я его не написал.

Цитата:

Заранее трудно предусмотреть))
Я по ходу путаться начал. Например, есть геттер какого-нибудь свойства, которое не просто возвращает значение, но тут же применяет к нему какие-то модификаторы (типа штрафов/бонусов от экипировки). Удобно для использования извне. Но если для внутренних расчётов требуется "чистое" свойство, то нужно держать это в голове и обращаться к "внутренней" переменной. Со временем, все эти нюансы из головы вымываются и начинается полная каша.

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

Wolsh 09.08.2018 00:21

Ну адекватные программисты конечно не станут пихать в геттер логику, логика и должна работать во "внутренних" единицах измерений; а вот конвертацию в другую валюту и т.п. — запросто. Не путай внутренние дела Класса с тем, что от него ждут. Например, класс для круговых диаграмм может вести все рассчеты в полярной системе координат (угол + длина), но выдавать точки в ортогональной (х, у) — пригодной для отрисовки. Или (похожая история) класс-компонент для выбора цвета в рисовалке все рассчеты "внутри" ведет в системе HSV, потому что это удобно для элементов управления, но на выходе от него ждут цвет в системе RGB, так как флэш работает в ней. И в конце рисования потребуют картинку в JPEG или PNG, хотя во время рисования эти форматы не использовались вообще никак.

caseyryan 09.08.2018 09:00

Цитата:

Сообщение от СлаваRa (Сообщение 1205844)
Код:

Второе.
обязательно, если вы хотите сломать полиморфизм :)

Что ты имеешь в виду?

Appleman 09.08.2018 10:13

Цитата:

Сообщение от Wolsh (Сообщение 1205848)
Ну адекватные программисты конечно не станут пихать в геттер логику

Посоветуйте, плиз, более адекватное решение. У меня пока как было. Нужно кому-то значение силы персонажа - обращаемся к экземпляру Character. Там условный "геттер" (на самом деле не геттер в чистом виде, а метод, принимающий ID нужного свойства, за что меня тут все мэтры нещадно критикуют) берёт текущее значение силы из свойств персонажа, проверяет модификаторы в менеджере статус-эффектов, проверяет модификаторы в менеджере экипировки, всё собирает, применяет и выдаёт вовне окончательное значение.

Если всю дополнительную логику убирать из геттера, то выходит, что нужно отдельно получать "чистое" значение свойства, а отдельно (другим методом) - его модификаторы и собирать всё уже на уровне класса, запрашивающего данные. Так? И если так, то во всех внешних объектах придётся добавлять логику, применяющую модификаторы к значениям.

Wolsh 09.08.2018 11:15

Ну, как-бы по-хорошему, свойства должны бы обновляться (пересчитываться) каждый раз при изменении того, от чего они зависят (надел кирасу — защитка пересчиталась, модификатор усталости при силовых атаках пересчитался и т.п.), а не в момент запроса. Тем более когда свойства зависят друг от друга: получается, запросили силу удара, а она зависит в том числе от эффекта каких-то зелий, но их еще не пересчитывали? Конечно же в идеале все свойства должны быть всегда актуальными на данный момент. Технически это означает, что перерасчеты делаются на уровне "сеттеров", а не в геттерах))) Когда что-то изменяется (сеттером, но не только), то сразу же изменяются все зависимые свойства. Как раз для сеттеров более чем нормально запускать какие-то логические цепочки, ведь в отличие от геттеров, сеттеры меняют значения внутренних свойств, то есть производят действия а не просто констатируют состояния каких-то свойств на данный момент. Геттер ни в коем случае не должен менять внутренние свойства, это противоречит всем здравым смыслам. Он может только представить какое-то свойство в другом формате и ВЫДАТЬ наружу модифицированное значение, не меняя внутреннее.

Tails 09.08.2018 12:25

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

Цитата:

Сообщение от СлаваRa (Сообщение 1205844)
Код:

Второе.
обязательно, если вы хотите сломать полиморфизм :)

Я озвучил тот вариант, что подойдёт в большинстве случаев. :)

Wormhole 09.08.2018 13:04

Цитата:

Сообщение от Wolsh (Сообщение 1205846)
Разные отклонения могут возникнуть при наследовании, например у наследников геттер станет возвращать преобразованное значение (не в сантиметрах а в дюймах например). Заранее трудно предусмотреть))

В чем проблема создать геттер у тех наследников, у кого он будет преобразовывать значение?

Цитата:

Сообщение от Wolsh (Сообщение 1205846)
Затем что он паблик, то есть свойство здесь read-only.

Сори, затупил. Пишу на Haxe довольно давно, уже привык к вот такому:
Код:

public var a(default, null):Int;
Добавлено через 5 минут
Цитата:

Сообщение от Appleman (Сообщение 1205851)
\Если всю дополнительную логику убирать из геттера, то выходит, что нужно отдельно получать "чистое" значение свойства, а отдельно (другим методом) - его модификаторы и собирать всё уже на уровне класса, запрашивающего данные. Так?

Вот так. Просто создать отдельный публичный метод calculateFinalStrength(). Свойство после этого можно и private сделать, если оно в чистом виде во внешнем коде не фигурирует.

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

Wolsh 09.08.2018 13:56

Цитата:

В чем проблема создать геттер у тех наследников, у кого он будет преобразовывать значение?
Как раз создать-то проблемы нет. Проблема будет, когда вместо долларов во ВНУТРЕННЕМ коде пройдут рубли из-за того, что использовался геттер this.money, выдающий _money, пересчитанный в рубли.

Добавлено через 6 минут
Цитата:

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

Wormhole 09.08.2018 16:03

Цитата:

Сообщение от Wolsh (Сообщение 1205856)
Проблема будет, когда вместо долларов во ВНУТРЕННЕМ коде пройдут рубли из-за того, что использовался геттер this.money, выдающий _money, пересчитанный в рубли.

Я не понял ситуацию, можно подробнее расписать?

Appleman 09.08.2018 17:41

Цитата:

Сообщение от Wolsh (Сообщение 1205852)
Ну, как-бы по-хорошему, свойства должны бы обновляться (пересчитываться) каждый раз при изменении того, от чего они зависят (надел кирасу — защитка пересчиталась, модификатор усталости при силовых атаках пересчитался и т.п.), а не в момент запроса. Тем более когда свойства зависят друг от друга

У нас есть свойство, которое по условиям игры неизменно от начала и до конца, пусть будет тот же интеллект. Но на него влияют статус-эффекты и снаряжение. Если например, шлем даёт +10%, а зелье - ещё +15%, означает ли это, что, надевая шлем, я должен пересчитывать оригинальное значение? И если у нас закончилось действие зелья, а шлем всё ещё на голове, то это же можно свихнуться с обратным пересчётом из процентов. Ведь база расчёта уже "уедет". Или мне придётся иметь одно "эталонное" значение в досье, а другое "оперативное" - в классе, которое я буду постоянно актуализировать?

Цитата:

Геттер ни в коем случае не должен менять внутренние свойства, это противоречит всем здравым смыслам. Он может только представить какое-то свойство в другом формате и ВЫДАТЬ наружу модифицированное значение, не меняя внутреннее.
Строго говоря, геттер у меня и не меняет ничего, только собирает инфо о фактически имеющихся на момент запроса факторах.

Цитата:

Сообщение от Wormhole (Сообщение 1205854)
Просто создать отдельный публичный метод calculateFinalStrength(). Свойство после этого можно и private сделать, если оно в чистом виде во внешнем коде не фигурирует.

Наверное, зря я на формулировку "геттер" давил. В твоём комментарии как раз и показан паблик внутри класса, который собирает все модификаторы и выдаёт итоговое значение. Согласись, если изменить сигнатуру на public function get finalStrength() : Number, то получится то, о чём я спрашивал :)

caseyryan 09.08.2018 20:36

Цитата:

Наверное, зря я на формулировку "геттер" давил.
Если речь пошла о формулировках, то геттер/сеттер на самом деле называется "свойством" (property). А переменные объявляемые на уровне экземпляра класса называются полями (fields / field variable). Это так, просто к сведению :D

Wolsh 09.08.2018 21:44

Цитата:

Я не понял ситуацию, можно подробнее расписать?
1. Прочитай первое сообщение в теме.
2. Итак, ситуация, когда в расчетах внутри класса используется не приват _someVar, а паблик геттер this.someVar (о чем и спрашивает топикстартер).. То есть, есть еще какие-то методы, оперирующие этим свойством в своих расчетах.. И вот мы создали наследника, который делает все то же самое, но результат (для внешнего кода) возвращает в других единицах измерения. Например, это какие-то денежные операции, и вся система работает в долларах, НО иногда клиенту требуется другая валюта, и мы подставляем другую стратегию вместо дефолтного супер-класса, который все хранит, считает и выдает в долларах: заменяем его наследником, который все считает и хранит в долларах, а результат отдает в пересчете на рубли (например). Не спрашивай сейчас, почему именно такая архитектура в данной задаче, я на ходу выдумываю. Итак, паблик геттер someVar отдает не _someVar, который в долларах, а _someVar * courseModifier. Что произойдет со всеми внутренними расчетами, которые вместо _someVar используют this.someVar? Они насчитают какую-то фигню, ибо вместо долларов будут получать рубли. Потому что блин паблик — он для внешней среды, это интерфейс вашего класса, его работа для других. А для себя существует приват, на что названия довольно недвусмысленно намекают.

Appleman 10.08.2018 01:08

caseyryan, буду знать, спасибо за ликбез :) Я верно понял, что свойство подразумевает доступность к нему извне, в отличие от полей?

Wolsh, как всегда всё прояснил и разложил по полочкам.

СлаваRa 10.08.2018 04:47

Берем базовый "абстрактный" класс, где геттер возвращает экземпляр объекта дефолтного поведения, который является NullObject, ссылка на который хранится в "холдере", а "сверху" геттер переопределен и возвращает ссылку на экземпляр конкретного объекта поведения. И 99% логики лежит в базовом классе, вот и подумайте теперь, что нужно использовать.

Или есть у вас базовая команда, у которой есть геттер isValid, который по дефолту возвращает _isValid, значение которого false, зачем _isValid не спрашивайте, тут много отговорок(IDE сгенерировал, "ну как же, есть геттер - значит нужен холдер" и т.д.), а каждый наследник переопределяет этот геттер и на основании чего-то выдает значение, а еще и геттеры-lazy, значение которых необходимо рассчивать при каждом обращении

и т.д. и т.д.

@Wolsh, а если все внутренние расчеты будут использовать методы/свойства, которые могут быть переопределены, тогда они все по идее вернут все в рублях и проблем не будет, но вот если будет использован, какой-то переопределенный метод, который может вернуть что-то в рублях, но вот вместо геттера, будет использован его холдер(который почему-то все хранит в долларах) - вот тут-то и будет ошибка. Я таких примеров за свою практику встречал сотни :)

Tails 10.08.2018 13:01

Дети, не ходите в программирование гулять)

Appleman 10.08.2018 17:55

Цитата:

Сообщение от Appleman (Сообщение 1205859)
У нас есть свойство персонажа, пусть будет тот же интеллект. На него влияют статус-эффекты и снаряжение. Если например, шлем даёт +10%, а зелье - ещё +15%, означает ли это, что, надевая шлем, я должен пересчитывать оригинальное значение? И если у нас закончилось действие зелья, а шлем всё ещё на голове, то это же можно свихнуться с обратным пересчётом из процентов. Ведь база расчёта уже изменится. Или мне придётся иметь одно "эталонное" значение в досье, а другое "оперативное" - в классе, которое я буду постоянно актуализировать?

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

Перенёс все поля во внутренний класс Parameters, вместе с сеттерами и геттерами. В Character имеем:
Код AS3:

private var _params: Parameters = new Parameters();

Но поле _intelligence в классе Character остаётся, в нём теперь будет храниться read-only значение интеллекта, но уже со всеми модификаторами. Забрать его можно геттером.

При изменении интеллекта (любым методом класса Character) не избежим вот такой записи, т.к. нет другого способа установки нового значения:
Код AS3:

_params.intelligence += value;

Сеттер в классе Parameters посылает событие об изменении значения (он его и так посылает, чтобы поймать и отразить изменения во Вью), это событие ловит класс Character (опять же он его и сейчас ловит). В приёмнике события ставим:

Код AS3:

_intelligence = _params.intelligence;
// Вызываем методы, проверяющие статусы и экипировку на предмет модификаторов интеллекта
// Их же вызываем при смене статусов или экипировки

Таким образом, больше не парюсь с актуальностью свойств и чётко понимаю, где брать модифицированное, а где - "чистое". Цена решения: фактическое дублирование всех полей и геттеров в и без того распухшем Character.

Что думаете?

GBee 10.08.2018 23:12

Можно сделать оригинальный Parameters со стартовыми значениями полей и активный Parameters, который пересчитывается исходя из стартового + модификаторы. И все это обернуть в какой-нить фасад (тот же Character).

А можно дублировать поля в таком же стиле. Короче вариантов миллион.

Appleman 11.08.2018 01:37

GBee, вот это ты мне удружил! Действительно, просто и гениально сделать 2 зеркальных класса. А я уже приготовился всю грядку напрямую в Character опять возвращать. Спасибо.

СлаваRa 11.08.2018 01:44

мне кажется все это уже можно было бы свернуть, если не заниматься брутофорс разработкой. Автор, стеньте первым в истории, кто изложит модель игры, правила и т.п. и потом начнем задавать вопросы про код, ну пожалуйста :)

Appleman 13.08.2018 09:54

СлаваRa, да надо бы, конечно, по-хорошему. Но диздок у меня весьма объёмный, при этом создавался он отнюдь не для демонстрации, поэтому его нужно сперва "прополоть" и "причесать". Мне представляется свинством выкатывать сюда такие "простыни", это как бы намекало уважаемой аудитории прочитать, что никто делать не нанимался.

Более того, завтра в отпуск улетаю, так что даже поучаствовать в обсуждении не смогу :)

ZergMaster 14.08.2018 13:24

можно по диздоку диаграммку зафигачить на draw.io - все должно получиться довольно лаконично и читабельно.


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

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