2

Say I have a collection of items, for example a List<Item>, and I need to check if any of the items is modified from a wrapper class, for example, a value of any of an item's propery is changed through a setter:

List<Item> items = itemsWrapper.getItems(); // itemsWrapper contains a list of items
Item anItem = items.get(index);
item.setProperty(property);
itemsWrapper.hasChanged(); // ??????

I am trying to figure out a way of achieving the last statement, or something similar.

I can see there are similar questions, like this one where using Hibernate is suggested but the answer is very vague, and my question is not DB related. It also mentions setting up a list of listeners, but I am not sure howt his could be done.

Or this other question, where the chosen answer suggests using a dirty flag that you need to rise every time you modify a property. However, my class would actually get dirtier, paradoxically, and I would need to modify dozens of methods that modify item's properties.

Is there another approach, best if it's transparent?

Community
  • 1
  • 1
dabadaba
  • 9,064
  • 21
  • 85
  • 155
  • 2
    "Observer" Pattern: itemsWrapper observes items for change. – Fildor Jun 24 '16 at 10:28
  • Can you give us more background? I suspect that you might want to be monitoring somewhere else for changes, rather than the collection itself. – Tim Biegeleisen Jun 24 '16 at 10:29
  • @Fildor I also checked out the observer pattern approach, but I don't see how that differs from the `dirty` flag approach in terms of actually "making my code dirtier" because I would still have to update all my setters (and object-modifying methods). – dabadaba Jun 24 '16 at 10:29
  • @TimBiegeleisen there's not much to it really. The actual scenario is not actually a `List` but something more specific for my project, but the idea I'm after is the same. I need to know if any of the items in a collection change (not if they're added or removed or replaced, but if the actual objects change). – dabadaba Jun 24 '16 at 10:31
  • Yes, you'll have to make changes to your code. Nearly no way around it. Otherwise you'd have to compare a certain state of all items against the actual. Which of course itself is a great deal of changed code ... – Fildor Jun 24 '16 at 10:31
  • The observer pattern also allows you to pass information about exactly _what_ is being updated, and unlike the ```dirty``` flag idea, you would not have to poll for changes. – Jorn Vernee Jun 24 '16 at 10:32
  • What is also unclear: When do you consider the collection **not** changed again. Will a call to `hasChanged()` reset the "changed" state? – Fildor Jun 24 '16 at 10:36
  • Or is it in fact more like you want to be informed _as soon as a change happens_ - more like an "event" - driven design? – Fildor Jun 24 '16 at 10:37
  • Another way would be to work with immutable Item objects. Any change would result in the whole object being replaced. So you could simply compare references. But there I am assuming they can be and are identified by something like a name or id ... – Fildor Jun 24 '16 at 10:42
  • @Fildor the reset to "not changed" would be made from a specific method, when this method is called the wrapping object is assumed to be in its "final state", therefore the state would now be unchanged. Further changes would then rise the flag again, and the changes should be registered. It's not an "event" approach. – dabadaba Jun 24 '16 at 10:42
  • @Fildor no, they're actually being compared by references, I am not using any id. – dabadaba Jun 24 '16 at 10:43

4 Answers4

1

The most transparent way is use Spring AOP AspectJ. Create your Aspect class and use @Before or @after annotations for your item.setProperty() method.

0

Simply, make the object private ad create getters and setters for it. This way you can get when the wrapper changes anything in the list.

If you want to detect, when a new element was put into the list, you can create a third, adder method to add elements to the array.

Bálint
  • 4,009
  • 2
  • 16
  • 27
0

The best way would be to use java.util.Observable Class which has a function boolean hasChanged() as your use-case says. This is a reference answer: When should we use Observer and Observable

Just a example:

<code>import java.util.Observable;
import java.util.Observer;
// First observer 
class FirstNewsReader implements Observer {
    public void update(Observable obj, Object arg) {
        System.out.println("FirstNewsReader got The news:"+(String)arg);
    }
}
//Second Observer
class SecondNewsReader implements Observer {
    public void update(Observable obj, Object arg) {
        System.out.println("SecondNewsReader got The news:"+(String)arg);
    }
}
// This is the class being observed.
class News extends Observable {
    void news() {
        String[] news = {"News 1", "News 2", "News 3"};
        for(String s: news){
            //set change
            setChanged();
            //notify observers for change
            notifyObservers(s);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("Error Occurred.");
            }
        }
    }
}
//Run Observer and Observable
class ObserverObservableDemo {
    public static void main(String args[]) {
        News observedNews = new News();
        FirstNewsReader reader1 = new FirstNewsReader();
        SecondNewsReader reader2 = new SecondNewsReader();
        observedNews.addObserver(reader1);
        observedNews.addObserver(reader2);
        observedNews.news();        
    }
}</code>
Community
  • 1
  • 1
Varshney P.
  • 208
  • 1
  • 12
  • Could you give a brief layout of the resulting layout of classes? I can't figure out how should I set it up. – dabadaba Jun 24 '16 at 16:42
0

Use the observer pattern for your Item/wrapper class. JavaFX (included since java 7.something) properties already support provide support for functionality like this.

Example:

class Item {

    private final ObjectProperty<Object> property = new SimpleObjectProperty<Object>();

    public final Object getProperty() {
        return this.property.get();
    }

    public final void setProperty(Object value) {
        this.property.set(value);
    }

    public final ObjectProperty<Object> propertyProperty() {
        return this.property;
    }
}
class Listener implements InvalidationListener {

    private boolean changed = false;

    @Override
    public void invalidated(Observable observable) {
        changed = true;
    }

    public boolean isChanged() {
        return changed;
    }

    public void resetChanged() {
        this.changed = false;
    }

}
// trigger update changes when the property changes
ObservableList<Item> list = FXCollections.observableArrayList(e -> new Observable[]{e.propertyProperty()});

list.addAll(new Item(), new Item());

Listener listener = new Listener();
list.addListener(listener);
System.out.println(listener.isChanged());

list.get(0).setProperty("");
System.out.println(listener.isChanged());
listener.resetChanged();

list.get(1).setProperty(1);
System.out.println(listener.isChanged());

listener.resetChanged();

list.add(new Item());
System.out.println(listener.isChanged());
fabian
  • 80,457
  • 12
  • 86
  • 114