Monday, 18 March 2013

Fickle - variables with attached events (in Java)

In programming a personal project I came across the requirement to have collections of objects that all have access to the same value. Another related requirement was that if the value changed then those other classes may have to reconfigure themselves (a knock-on effect due to that original value changing). The obvious answer was to employ an event driven design.
I created a class to handle these requirements in a generic way. I thought of this new "value class" as containing a capricious value, and so called it Fickle, which is a parameterized class. It is not synchronised, so beware when multithreading.

Here is the code:
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;

public class Fickle<T>
{
    private T _value;
    private final Set<OnChangeListener<T>> _listeners;

    public Fickle(final T value)
    {
        _value = value;
        _listeners = Collections.newSetFromMap(
         new WeakHashMap<OnChangeListener<T>, Boolean>());
    }

    public void addOnChangeListener(final OnChangeListener<T> listener)
    {
        _listeners.add(listener);
    }

    public void setValue(final T newValue)
    {
        _value = newValue;
        for (OnChangeListener<T> l : _listeners)
            if (l != null)
                l.onChangeEvent(newValue);
    }

    public T getValue()
    {
        return _value;
    }

    public interface OnChangeListener<T>
    {
        void onChangeEvent(T newValue);
    }
}
As you can see I have used a Set made from a WeakHashMap to store the event listeners. This is to ensure that Fickle objects don't create memory leaks. As the reference to the listener is weak, when a listener object is no longer in use anywhere else in the code then it will automatically be removed from the set. This is a very cool Java feature. However, what this means is that you must have a strong (normal) reference to the listener object, otherwise it will be deleted.

Here is an example of how to use the Fickle class:
class MrsClass
{
    private float _doubleVal;

    public MrsClass(final Fickle<Float> capVal)
    {
        // Initialize the member variable.
        setVal(capVal.getValue());

        // Handle the on change event.
        capVal.addOnChangeListener(new Fickle.OnChangeListener<Float>()
            {
                public void onChangeEvent(Float newValue)
                {
                    setVal(newValue);
                }
            });
    }
    
    private void setVal(float val)
    {
        _doubleVal = val * 2.0f;
    }

    public float getDoubleVal()
    {
        return _doubleVal;
    }
}

class MrMain
{
    public static void main(String[] args)
    {
        Fickle<Float> val;
        val = new Fickle<Float>(1.0f);

        MrsClass mrs = new MrsClass(val);
        float doubled = mrs.getDoubleVal(); // should equal 2.0f

        val.setValue(3.5f);
        doubled = mrs.getDoubleVal(); // should equal 7.0f

        // By dereferencing the MrsClass object the
        // listener will be automatically removed from
        // the listener Set by the WeakHashMap class.
        mrs = null;
    }
}
So this is effectively an encapsulation of the Observer pattern.

This code is completely free to use by anybody. It is held under the Do What You Want To Public License: http://tinyurl.com/DWYWTPL

No comments:

Post a Comment