javafx阴影_JavaFX技巧来节省内存! 属性和可观察对象的阴影场

javafx阴影

JavaFX的世界中, Properties API允许UI开发人员将值绑定到UI控件。 这种功能出奇的简单,但是当对象模型经常使用属性时,应用程序可能会很快耗尽内存。 我通常会编写两个单独的对象,例如pojo类和表示模型对象。 此技术通常在基于Swing的应用程序中使用。 从JavaFX角度来看,您可以只创建一个具有属性的对象,以允许观察者(侦听器)更新值。 听起来不错吧? 不完全是因为主要问题是当所有对象的(pojo)属性(字段)都是还包装实际值的属性时,程序员(api的用户)可能根本不想绑定或使用属性,而只想访问实际值。 那么,JavaFX开发人员该做什么?

一个TreeTable控件持有雇员。

一个TreeTable控件持有雇员。

我经常访问Dirk Lemmermann的博客Pixel Perfect ,该博客经常发布非常有用的JavaFX技巧。 最近,Dirk在博客中发布了有关如何使用名为Shadow Fields的有趣模式来节省内存的信息。 要查看他的帖子,请访问他的博客条目JavaFX Tip 23:节省内存! 属性的阴影字段。 。 Dirk的JavaFX技巧确实有助于解决上述问题(减少堆),但是我注意到必须存在样板代码才能( 聪明地 确定 )为调用者提供返回值是实际对象还是属性包装器对象。 例如,该代码将返回intInteger值,而不是在调用getset方法时返回IntegerProperty对象,从而节省了内存。 此外,代码声明了两个变量来保存两种值类型之一。 例如:

private String _title = "Untitled"; // shadow field
private StringProperty title;

我觉得我可以使事情更简洁,并可能节省更多的内存。 并减少样板代码。 我决定使用Java 8的默认方法创建一个接口,该接口将处理管理实际值和属性。 API的用户将只创建一个实现以下接口的域类:

界面属性访问器

一个提供访问器方法的接口,以提供对实际对象值或JavaFX属性包装器对象的智能确定。 API的用户必须实现一种称为getModelProperties()的方法,该方法返回属性名(字符串)和值(对象)的映射。 该值可以是实际对象或属性类型对象。 下面的代码也将支持可观察列表。

package com.jfxbe;

import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Provide default methods to support the similar
 * capability of the shadow fields pattern.
 * To save memory object values don't have to be
 * wrapped into a Property object when using getters
 * and setters, however when calling property type methods
 * values will be wrapped into a property object.
 *
 * Default methods for Observable lists are provided too.
 *
 * Created by cpdea on 4/3/16.
 */
public interface PropertyAccessors {

    Map<String, Object> getModelProperties();

    default <T> T getValue(String name, Object defaultVal) {
        Object p = getModelProperties().get(name);
        p = p==null ? defaultVal : p;
        return (T) ((p instanceof Property) ? ((Property) p).getValue(): p);
    }

    default void setValue(String name, Object value) {
        Object p = getModelProperties().get(name);
        if (p instanceof Property) {
            ((Property)p).setValue(value);
        } else {
            getModelProperties().put(name, value);
        }
    }

    default <T> T refProperty(String name, Class propClass, Class rawValType) {
        Object p = getModelProperties().get(name);
        Property prop = null;

        try {

            if (p == null) {
                Class[] constructorTypes =
                        new Class[]{Object.class, String.class};
                Constructor<Property> propConstr =
                        propClass.getDeclaredConstructor(constructorTypes);
                prop = propConstr.newInstance(this, name);
            } else if (rawValType.isInstance(p)) {
                Class[] constructorTypes = new Class[]{Object.class,
                        String.class, rawValType};
                Constructor<Property> propConstr =
                        propClass.getDeclaredConstructor(constructorTypes);
                prop = propConstr.newInstance(this, name, p);
            } else {
                prop = (Property) p;
            }
            getModelProperties().put(name, prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) prop;
    }

    default <T> List<T> getValues(String name, List<T> defaultValue) {
        Object p, o = getModelProperties().get(name);
        p = o;
        o = o==null ? defaultValue : o;
        if (!o.equals(p)) {
            getModelProperties().put(name, o);
        }
        return  (List<T>) o;
    }

    default <T> void setValues(String name, List<T> newList) {
        Object list = getModelProperties().get(name);
        if (list == null || !(list instanceof ObservableList)) {
            getModelProperties().put(name, newList);
        } else {
            // Should the list be totally replaced? below clears and adds all items
            ObservableList<T> observableList = (ObservableList<T>) list;
            observableList.clear();
            observableList.addAll(newList);
        }
    }

    default <T> ObservableList<T> refObservables(String name) {
        List list = (List) getModelProperties().get(name);

        if (list == null) {
            list = FXCollections.observableArrayList(getValues(name, new ArrayList<>()));
            getModelProperties().put(name, list);
        }

        if (! (list instanceof ObservableList)) {
            list = FXCollections.observableArrayList(list);
            getModelProperties().put(name, list);
        }

        return (ObservableList<T>) list;
    }
}

员工阶层

一个名为Employee的类,它实现PropertyAccessor接口。 在下面,您会注意到每个字段的属性名称都是使用public static final String声明的 。 例如,员工的名字是:

public static final String NAME_PROPERTY = “name”;

对于诸如getter,setter和xyzProperty()之类的访问器方法,您会在PropertyAccessor接口中注意到对默认方法的调用。

package com.jfxbe;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A hybrid domain and model object using the shadow field pattern to save memory.
 * Created by cpdea
 */
public class Employee implements PropertyAccessors{

    /** This is a map to hold properties and observables */
    private Map<String, Object> modelProperties;

    public static final String NAME_PROPERTY = "name";
    public static final String POWERS_PROPERTY = "powers";
    public static final String SUPERVISOR_PROPERTY = "supervisor";
    public static final String MINIONS_PROPERTY = "minions";

    public Employee(String name, String powers) {
        setName(name);
        setPowers(powers);
    }

    @Override
    public Map<String, Object> getModelProperties() {
        if (modelProperties == null) {
            modelProperties = new HashMap<>();
        }
        return modelProperties;
    }

    public final String getName() {
        return getValue(NAME_PROPERTY, "");
    }
    public final void setName(String name) {
        setValue(NAME_PROPERTY, name);
    }
    public final StringProperty nameProperty() {
        return refProperty(NAME_PROPERTY, SimpleStringProperty.class, String.class);
    }

    public String getPowers() {
        return getValue(POWERS_PROPERTY, "");
    }

    public final StringProperty powersProperty() {
        return refProperty(POWERS_PROPERTY, StringProperty.class, String.class);
    }

    public final void setPowers(String powers) {
        setValue(POWERS_PROPERTY, powers);
    }

    public final Employee getSupervisor() {
        return getValue(SUPERVISOR_PROPERTY, null);
    }

    public final ObjectProperty<Employee> supervisorProperty() {
        return refProperty(SUPERVISOR_PROPERTY, ObjectProperty.class, Employee.class);
    }

    public final void setSupervisor(Employee supervisor) {
        setValue(SUPERVISOR_PROPERTY, supervisor);
    }

    public final List<Employee> getMinions() {
        return getValues(MINIONS_PROPERTY, new ArrayList<>());
    }

    public final ObservableList<Employee> minionsObservables() {
        return refObservables(MINIONS_PROPERTY);
    }

    public final void setMinions(List<Employee> minions) {
        setValues(MINIONS_PROPERTY, minions);
    }

}

结论

因此,您已找到解决方案,尝试消除两个变量和其他样板代码。 我实际上并没有使用大量数据来测试代码,因此也许在另一篇文章中或某个幸运的读者会创建一个测试,以比较所有三个(对象具有所有属性,Dirk和mine的)实现。

与RMI服务器一起使用时,此方法的可能缺点可能是序列化对象。 我敢肯定还有其他可能的缺点,但是对于大多数用例而言,这可能更容易处理和更简洁。

请随意发表评论!

参考资料

翻译自: https://www.javacodegeeks.com/2016/04/javafx-tips-save-memory-shadow-fields-properties-observables.html

javafx阴影

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值