Java: как сделать попроще?

0000

Есть несколько (~10) классов, у которых есть float-аттрибуты.
Хочется для объектов этого класса и этих атрибутов возможность задавать как будет меняться атрибут по времени, т.е. что-то вроде

Объект.Атрибут = f(t);

Например, задав такую функцию, значение атрибута плавно увеличивается по времени выполнения программы до некоторого значения и становится постоянным.
Пока придумал только заменить атрибуты с float на класс, реализующий логику Функции.

class Eval
{
... // набор атрибутов и методов, заменяя которые можно менять тип фунции
public float currValue;
static update (float msDelta); // здесь пробегаем по всем объектам Eval и в currValue меняем значение; дергается в главном цикле программы.
}

class A
{
Eval posX;
}

A a = new A(...);
posX = new Eval(...);

Не нравится то, что будет много объектов класса Eval, которые по сути будут реализовать константу, но их все равно придется каждый раз проверять в update.
В итоге может получится не совсем быстро.
Может кто подскажет как попроще можно сделать?
P.S. Книгу по Java не сильно толстую и толковую посоветуйте еще.

katrin2201

Не нравится то, что будет много объектов класса Eval, которые по сути будут реализовать константу, но их все равно придется каждый раз проверять в update.
В итоге может получится не совсем быстро.
Оверхед по цпу будет, но очень мизерный, порядка десятков наносекунд.
По памяти тоже это можно все оптимизировывать, но там оверхед типа 16 байт на один объект.
Я не думаю, что это все представляет какую-то проблему.
Может кто подскажет как попроще можно сделать?
Я бы не делал стейтфул классы Eval, а просто сделал бы у них один метод
public float getValueAt(float msDelta);
Если ты хочешь все типа красиво без оверхедов, то делай Eval интерфейсом, и несколько его реализаций - ConstantEval, ArrayEval, ... .

Dimon89

Создай вспомогательный класс-аниматор, который будет реализовывать эту логику. Просто, удобно и единообразно.

0000

public float getValueAt(float msDelta);
Не подойдет. Я как обычно не все расписал, что надо - значение должно управляться меняться сразу после привязки (то есть у Eval надо будет добавить public void start т.е. выдаваемое значение должно зависеть не от системного времени, а от момента привязки и системного времени.
, чего то не нашел шаблон проектирования аниматор. Может у него есть альтернативное название?
А как еще вот такой вариант: сделать через reflection

class Eval
{
...
private Array<Eval> EvalList = new Array<Eval>;
private Object obj;
private String fieldName;
static add(Object obj, String fieldName, ..);
static void update(float msDelta); // пробегаем по всем объектам Eval и через reflection меняем значения у целевых атрибутов сторонних классов
}

A a = new A;
Eval.add(a, "posX", ..)

Насколько я понимаю в Java можно хранить ссыль только на объект/метод, а не на простой тип, поэтому в update каждый раз придется через reflection лазить и искать нужное поле у стороннего объекта?

Dasar

лучше делать без reflection-а - будет существенно быстрее, например, как-то так (это с#, но на java будет очень похоже)


class A
{
public float age;
public float weight;
}
class B
{
public float width;
public float height;
public A a1 = new A;
}

class Program
{
static void Main
{
var a= new A;
var b = new B;

var evaluator = new Evaluator;
evaluator.Bind=>a.age, v=>a.age=v, (v, context)=>v + 1);
evaluator.Bind=>b.a1.weight, v=>b.a1.weight=v, (v, context)=>v * 1.1);

for (var i = 0; i < 100; ++i)
{
evaluator.Update;
Console.WriteLine(b.a1.weight);
}
}
}
class Evaluator
{
List<Func<float, EvaluatorContext, float>> bindings = new List<Func<float, EvaluatorContext, float>>
void Bind(Func<float> getter, Func<float> setter, Func<float, EvaluatorContext, float> f)
{
bindings.Add(new Binding{getter = getter, setter = setter, f = f});
}
public void Update
{
var context = new EvaluatorContext;
foreach (var binging in bindings)
{
var v = binding.getter;
v = binding.f(v, context);
setter(v);
}
}

class Binding
{
public Func<float> getter;
public Func<float> setter;
public Func<float, EvaluatorContext, float> f;
}
}
class EvaluatorContext
{
}


EvaluatorContext добавлен для передачи данных из Evaluator в функцию, например, для передачи информации о времени привязки Binding-а, или о том, что надо всё начать с начала и т.д.

Dimon89

Шаблон - Visitor. Аниматор - это идея. Класс, который анимирует объект другого класса, устанавливая ему некоторое свойство по заданному алгоритму и меняет его по времени.
При этом получается такая структура:
1) Драйвер - это либо таймер, либо некоторый объект, который содержит список аниматоров и регулярно дёргает у каждого метод update(currentMs) или step(ms) - смотря как твоему алгоритму удобней.
2) Аниматор - это визитор с одним публичным методом update или step (см. выше. который вычисляет новое значение по заданному алгоритму и дёргает setValue своему объекту. Вычисление значения можно вынести в абстрактный метод calculateValue(currentMs/stepMs) и перекрывать только его.
3) Целевой объект. Должен предоставлять интерфейс getValue/setValue.
В коде это может выглядеть, например, так:


MyObject animated;
animationTimer.addAnimator(new Animator(object) {
protected int calculateValue(int stepMs) {
return (getCurrentValue + stepMs) % 1000;
}
}

yroslavasako

можно посмотреть как сделано в каком-нибудь javafx, там исходники открыты.

0000

, не совсем идею уловил. А описание класса Func не требуется разве?
,
3) Целевой объект. Должен предоставлять интерфейс getValue/setValue.
То есть для каждого атрибута, для которого надо иметь возможность менять его, надо добавить в класс сеттер и геттер?
Чет не могу пока проникнуться шаблоном Visitor. Читаю тут - http://www.williamspublishing.com/PDF/5-8459-0393-9/part.pdf Подозрение, что он мне не очень подходит: насколько я понял его рекомендуют применять, если для нескольких классов надо реализовать одну логику, но в моем то случае надо реализовать логику не для класса, а для любого численного атрибута. Хотя я еще до конца не осознал, что происходит, так что версия о том, что не подходит - запросто тупняком может оказаться.
Reflection совсем просто получилось:

Field f = obj.class.getDeclaredField(fieldName);
f.set(obj, currValue);

Field для различных классов можно захешировать в Map или даже все возможные значения при инициализации проги прописать и тогда update будет дергать только f.set(... что по идее не сильно на производительности скажется.
Для каждой функции свой класс писать не хочется, ибо там отличия минимальны и все через case можно разрулить.
P.S. Пошел дальше проникаться шаблоном.

Dasar

А описание класса Func не требуется разве?
В c# Func - это тип из стандартной библиотеки. В Java - это будет java.util.function.Function

luna89

Например, задав такую функцию, значение атрибута плавно увеличивается по времени выполнения программы до некоторого значения и становится постоянным
Я бы не стал так делать. Если данные меняются сами по себе, то трудно будет потом понять, как прога работает. ЕМНИП, таймеры в джаве реализованы через потоки, так что еще могут возникнуть проблемы с concurrency. Я бы подумал, к какому месту программы относится вся эта логика, и реализовал бы ее в этом месте.

0000

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

kokoc88

типа игрушка для сотовых
Это называется tweener, найди готовый или посмотри, как сделаны популярные для Java или других языков.

0000

Спасибо. Да, готовая либа как оказалось под это дело есть.
Вот так, еще чуть-чуть и родился бы велосипед!
Оставить комментарий
Имя или ник:
Комментарий: