Просмотр полной версии : Проверить на наличие геттера
Appleman
19.07.2018, 15:42
Друзья, подскажите, пожалуйста, как проверить наличие в классе геттера, зная строку для обращения? Попытка написать:
trace(SomeClass.hasOwnProperty(PropID))
выдаёт false, даже если геттер в нём прописан. Больше ничего дельного в голову не приходит.
Отказаться от использования столь ужасного подхода к программированию?
alexandrratush
19.07.2018, 18:30
Какая задача стоит перед вами? Вам может помочь рефлексия.
Ну какая, вестимо поверить наличие геттера в объекте по строковому ключу.
Appleman
20.07.2018, 10:04
Решил. Оказывается, если геттер не объявлен как public, то ничего не получается. Изначально был с модификатором internal.
Отказаться от использования столь ужасного подхода к программированию?
Это процесс не быстрый :)
Чем дольше вы тяните, тем масштабнее будут проблемы и демотивация, но я могу лишь советовать :)
Appleman
20.07.2018, 12:38
СлаваRa, ко мне можно и нужно на "ты" :)
Я же не специально... У меня первый проект, я уже год делаю его для себя, в своё удовольствие и в меру собственных способностей. Расширение кругозора происходит стремительно, поэтому уже раза 2 почти полностью переписывал. И скорее всего, это не последний раз.
Если конкретно по обсуждаемому вопросу, но ноги растут вот отсюда (http://www.flasher.ru/forum/showpost.php?p=1205721&postcount=16). После того как я распихал многочисленные свойства класса Character по нескольким подключаемым классам, возникла проблема с обращениями к ним извне. Они у меня происходят через передачу строкового ID нужного свойства. Раньше я спокойно писал в любом месте, где есть ссылка на экземпляр персонажа: character[PropID], а теперь не могу, так как теперь свойство propID может быть как в самом классе Character, так и в Skills, Personality, Statuses и т.п.
На сегодня лучшее, что по этому поводу придумалось - это сделать единый метод, который будет проверять, где "живёт" запрашиваемое свойство, и выдавать его значение. Заодно применять к нему модификаторы, "подрезать" его при необходимости по установленному максимуму и т.п. Вот такой код:
public function getProp (propID: String) : * // Возвращает значение свойства по ID
{
if (this.hasOwnProperty(propID)) return this[propID];
var componentID: String = this.redirectByPropID(propID);
return this[componentID][propID];
}
protected function redirectByPropID(propID: String) : String // Возвращает ID компонента, хранящего свойство
{
if (_personality.hasOwnProperty(propID)) return ChIDs.COMPONENT_PERSONALITY;
if (_parameters.hasOwnProperty(propID)) return ChIDs.COMPONENT_PARAMETERS;
if (_skills.hasOwnProperty(propID)) return ChIDs.COMPONENT_SKILLS;
throw (CLASS_DEBUG_NAME + "-> redirectByPropID: неизвестный ID параметра. Используйте константы класса ID персонажей!");
}
Если есть что покритиковать и предложить, буду признателен.
caseyryan
27.07.2018, 22:09
Не обращая внимания на весь этот ужас в коде, все же спрошу)
А что если у тебя и _personality и _parameters будет содержать свойство propID?
Какое-то странное условие, мягко говоря.
И кстати, на сколько я помню, такая конструкция может легко отвалиться с ошибкой, если класс не динамический
Appleman
28.07.2018, 00:15
Не обращая внимания на весь этот ужас в коде, все же спрошу)
Ужас - в самом подходе или в его реализации? Не стесняйся, мне самому не нравится, но ничего лучше не придумалось пока.
А что если у тебя и _personality и _parameters будет содержать свойство propID?
Какое-то странное условие, мягко говоря.
Вообще такого быть не должно, так как я изначально разделил разные свойства персонажа на несколько блоков. Но теоретически, конечно, будет косяк, т.к. всегда будет возвращаться ссылка на первый проверяемый компонент.
И кстати, на сколько я помню, такая конструкция может легко отвалиться с ошибкой, если класс не динамический
Не, классы не динамические, всё работает. Главное, чтобы геттеры внутри имели модификатор public. Со всеми остальными hasOwnProperty выдаёт false.
с таким подходом не возможно:
- рефакторинг
- обнаружение синтаксисческих ошибок на этапе компиляции
- обнаружение ошибок логики(вопрос выше про совпадение имен)
- нормальная работа IDE
- ...
caseyryan
28.07.2018, 17:31
Ужас - в самом подходе или в его реализации? Не стесняйся, мне самому не нравится, но ничего лучше не придумалось пока.
Не совсем понимаю что ты хочешь сделать, но если у тебя эти классы унаследорваны от разных базовых классов, но свойства должно присутствовать обязательно, то почему ты не используешь интерфейсы?
Appleman
29.07.2018, 23:05
СлаваRa, caseyryan, согласен, подход - дерьмо.
Не совсем понимаю что ты хочешь сделать, но если у тебя эти классы унаследованы от разных базовых классов, но свойства должно присутствовать обязательно, то почему ты не используешь интерфейсы?
Да они вообще ни от чего не унаследованы. В каждом - пачка свойств, а также сеттеров/геттеров. Возможно, когда добавятся какие-то методы обработки, обязательные для всех компонентов, тогда и потребуются интерфейсы для пущей уверенности.
Давайте я попробую привести ещё пример и задать вопрос.
Изначально в классе Character были прописаны свойства из "досье", которые остаются неизменными (сила, интеллект), свойства, отслеживающих текущие параметры (энергия, ХП), скиллы. От такой огромной простыни (сами свойства, сеттеры/геттеры, методы обработки) у меня класс сильно "распух". Чтобы всё это хозяйство организовать, придумалось раскидать свойства не нескольким новым классам. Но возникла новая проблема. У меня происходит много обращений за значениями свойств, переданных как параметры. Наиболее типичный случай - класс проверки условий доступности игровых действий. Вот его пример:
public class ACPropEquality extends ACEntity
{
private var _value: * ;
public function ACPropEquality(newOwnerID: String, newPropID: String, newValue: *)
{
super(newOwnerID, newPropID);
_value = newValue;
}
override public function testCondition(action:ActionEntity) : Boolean
{
super.getOwner(action);
return (_owner.getProp(_propID) == _value);
}
}
Получается, что под newPropID может быть свойство из досье, текущий параметр или скилл. Я рассуждал так, что другие классы не должны "знать" и "вникать", где конкретно в классе персонажа "живёт" запрашиваемый параметр, а просто передать ID свойства и получить назад текущее значение. В этом случае встаёт вопрос, как в самом Character идентифицировать, где искать свойство под полученным ID. Результат - как видели выше.
Или я вас не правильно понял, и главный объект критики - это подход, при котором запросы текущих значений свойств сделаны через их строковые идентификаторы?
Или я вас не правильно понял, и главный объект критики - это подход, при котором запросы текущих значений свойств сделаны через их строковые идентификаторы?
бинго!
Appleman
30.07.2018, 15:52
Отлично! Друзья, я вам буду очень признателен за любую наводку на более "красивое" решение.
Я попытался максимально компактно и доходчиво сформулировать ниже, почему у меня активно применяются строковые ID.
Класс игрового действия состоит из 3-х компонентов: справочных данных (ActionData), а также 2-х персонажей, один из которых - "актор" (кто активно действует), а второй - "реципиент" (кто принимает эффект от действия). Вот смотрите кусочек класса ActionEntity:
protected var _actionData: ADEntity; // Данные выполняемого действия
protected var _actor: Character; // Актор - активный участник, совершающий действие
protected var _recipient: Character; // Реципиент - пассивный участник, получающий эффект от действия
public function ActionEntity(data: ADEntity, character1: Character, character2: Character = null)
{
_actionData = data;
this.recognizeRoles(character1, character2);
}
Обратите внимание, что если, например, действие рассчитано на одного персонажа (например, применить на себя какой-нибудь предмет), то в таком случае в конструктор будет передан один персонаж, который будет и актором, и реципиентом. С этим разбирается метод recognizeRoles(). Он запускается прямо в конструкторе и сохраняет полученных персонажей в абстрактных переменных _actor и _recipient.
ActionData (наследник класса ADEntity) - содержит "справочные" данные действия. Они неизменны, и теоретически их можно (и наверное нужно) вообще из кода перенести куда-нибудь, но сейчас не об этом. Важно, что там формулируются условия нескольких видов: критические (при их невыполнении действие не будет создано), видимости (действие не будет выведено игроку) и активности (действие будет неактивно). Вот как это сделано сейчас:
public class ADEntity
{
protected var _criticalConditions: Vector.<ACEntity> = new Vector.<ACEntity>;
protected var _visibleConditions: Vector.<ACEntity> = new Vector.<ACEntity>;
protected var _activeConditions: Vector.<ACEntity> = new Vector.<ACEntity>;
}
В каждом векторе - набор одновременно выполняемых условий - наследники класса ACEntity. В своём предыдущем сообщении я приводил код одного из его наследников - класса ACPropEquality, проверяющего свойство на равенство указанному значению.
Вот маленький кусочек кода, где создаётся действие рукопашной атаки:
_act = new ADDoubleMelee(ActionIDs.MELEE);
_act.criticalConditions = new <ACEntity>
[
new ACPropEquality(ActionIDs.OWNER_ACTOR, ChIDs.RPG_CLASS, ChIDs.CLASS_FIGHTER)
];
Загвоздка в том, что, будучи прописанными в классе ADEntity, условия "не знают" и "не могут знать", что за действующие лица попадут в конструктор ActionEntity. Как мне сформулировать необходимость проверки каких-то свойств экземпляров Character в классе, не имеющим на них ссылок. Вот я и выкрутился со строковыми идентификаторами.
В классе ActionEntity запуск проверки условий выглядит так:
public function testVisibleConditions() : Boolean
{
for each (var cond: ACEntity in this._actionData.visibleConditions)
{
if (!cond.testCondition(this)) return false;
}
return true;
}
Если сможете сказать, как в таком раскладе обойтись без использования строковых ID, я буду очень благодарен.
Добавлено через 21 час 11 минут
Посидел-подумал... Вот такой вариант проверки условий придумался без использования строковых идентификаторов.
public class ConditionStrength
{
private var _measureMin: Number;
private var _measureMax: Number;
public function ConditionStrength(measureMin: Number = 0, measureMax: Number = 0)
{
_measureMin = measureMin;
_measureMax = measureMax;
}
public function testCondition(ch: Character) : Boolean
{
if (ch.strength >= _measureMin && ch.strength < _measureMax) return true;
return false;
}
}
Что скажете?
Wormhole
04.08.2018, 21:32
Не читал последнее сообщение, но
обращайся напрямую через компоненты, например: character.progression.xp
Либо создай псевдо-свойства в базовом классе с геттерами, которые будут вызывать геттеры соответствующего свойства в его компоненте
Добавлено через 3 минуты
Но на компоненты имеет смысл делить, когда есть еще и дополнительная функциональность, связанная с этими свойствами, иначе получаем запах "Data Object".
Если ее нет, но свойств слишком много, проверь принцип одной обязанности - класс должен отвечать только за что-то одно.
Например, раньше у меня класс игрока содержал две абсолютно несвязанные друг с другом части - представление прогресса игрока и его раскачки и представление игрока в бою (с хп, маной и т. д.). Потом я их разделил на два класса
Вроде интерфейсы прям-таки и напрашиваются.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.