1

I implemented the Observer pattern in an MVC application so a bean can listen for changes in the model. Since I'm in Java8 I'm using Observer and Observable from Java.util. Here's part of what I coded:

import java.util.Observable;
import java.util.Observer;

public class UserBean implements Observer{
   private Integer userId;
   private String userName;
   private String userEmail;
   /* getters and setters */
  
   @Override
   public void update(Observable o, Object object) {
       if(object instanceof Integer)
           setUserId((Integer)object);
       else if(object instanceof String)
           setUserName((String)object);
   }
}

public class UserModel extends Observable {

    private Integer id;
    private String name;
    private String email;
    /* getters */
    public void setId(Integer id){
        this.id = id;
        setChanged();
        notifyObservers(id);
    }

    public void setName(String name){
        this.name = name;
        setChanged();
        notifyObservers(name);
    }

    public void setEmail(String email){
        this.name = email;
        // how can i listen for this change?
        /*setChanged();
        notifyObservers(email);*/
    }
}

I think the implementation is correct because, in a simple test that I made, the changes to Ids and Names are correctly read by the bean class, but I realized that I can't listen for changes of the email attributes because in the bean class I'm using 'instanceof' to understand which value I have to update.

There's a way to recognize which variable has changed? I should implement my own version of the Observer and Observable classes instead of using Java.Util? I think that with the second solution I could define an update method for every attribute but I don't know how to manage the synchronization process in Java.

  • you can also notify the observer using a DTO which brings some extra info about the attribute – fantaghirocco Sep 08 '20 at 09:23
  • Don't use j.u.Observer and Observable. They are terribly outdated. A proper interface definition should make use of generics; they were written before generics existed. – Michael Sep 08 '20 at 09:26
  • 1
    following @Michael advice: https://stackoverflow.com/questions/46380073/observer-is-deprecated-in-java-9-what-should-we-use-instead-of-it – fantaghirocco Sep 08 '20 at 09:30
  • @fantaghirocco Oh, nice. I almost said in my last comment that they should go ahead and deprecate it already. Glad they have. That one passed me by :) – Michael Sep 08 '20 at 09:32
  • @fantaghirocco could you explain better? My professor doesn't explain to us all the existing patterns and I don't know DTO (I just google it). – Andrea Efficace Sep 08 '20 at 09:33
  • @Michael Yeah, I know they are deprecated that's why I specified that I'm using Java8. This project is just a didactic project and I don't need to go further than Java8. Using the solution linked by fantaghiricco I could resolve my problem? – Andrea Efficace Sep 08 '20 at 09:36
  • @AndreaEfficace The reasons they were deprecated in Java 9 are still applicable to Java 8, 7, 6, 5... Java 9 just happens to be the point when the JDK authors decided to publicly declare "don't use this any more". – Michael Sep 08 '20 at 09:50

2 Answers2

2

The first argument in the update method is an Observable. That is your UserModel object that has changed, so it contains all the updated data. So instead of using the second parameter to pass the new value of an object you can use it to pass the name of the object that has changed (or use an enum because it's a bit cleaner).

A solution could look like this:

UserBean:

import java.util.Observable;
import java.util.Observer;

import observer.UserModel.ChangedValue;

public class UserBean implements Observer {
    
    private Integer userId;
    private String userName;
    private String userEmail;
    /* getters and setters */
    
    @Override
    public void update(Observable o, Object object) {
        if (o instanceof UserModel && object instanceof ChangedValue) {
            UserModel userModel = (UserModel) o;
            ChangedValue changed = (ChangedValue) object;
            
            switch (changed) {
                case EMAIL:
                    setEmail(userModel.getEmail());
                    break;
                case ID:
                    setUserId(userModel.getId());
                    break;
                case NAME:
                    setUserName(userModel.getName());
                    break;
                default:
                    throw new IllegalStateException("Unexpected ChangedValue type: " + changed);
                
            }
        }
    }
    
    //...
}

UserModel:

import java.util.Observable;

public class UserModel extends Observable {
    
    public enum ChangedValue {
        ID, //
        NAME, //
        EMAIL, //
        //...
    }
    
    private Integer id;
    private String name;
    private String email;
    
    //...
    
    public void setId(Integer id) {
        this.id = id;
        setChanged();
        notifyObservers(ChangedValue.ID);//use the enum types as parameters here
    }
    
    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers(ChangedValue.NAME);
    }
    
    public void setEmail(String email) {
        this.name = email;
        setChanged();
        notifyObservers(ChangedValue.EMAIL);
    }
}

Note: Like mentioned in the comments a generic approach would be better, to avoid the object casts. It could be used similar to this implementation. Just add some generic parameters, but the idea stays the same.

Tobias
  • 2,547
  • 3
  • 14
  • 29
2

I mean something like this:

public class UserModel extends Observable {
    
    enum AttributeKind {
        ID, NAME, EMAIL;
    }
    
    class UserModelDescriptor {
        public AttributeKind attributeKind;
        public Object attribute;
        
        public UserModelDescriptor(AttributeKind attributeKind, Object attribute) {
            super();
            this.attributeKind = attributeKind;
            this.attribute = attribute;
        }
    }

    private Integer id;
    private String name;
    private String email;
    /* getters */
    public void setId(Integer id){
        this.id = id;
        setChanged();
        notifyObservers(new UserModelDescriptor(AttributeKind.ID, id));
    }
        . . .
}

The observer:

public class UserBean implements Observer{

    @Override
    public void update(Observable o, Object arg) {
        UserModelDescriptor descriptor = (UserModelDescriptor)arg;
        
        switch (descriptor.attributeKind) {
            case ID:
                int id = (Integer)descriptor.attribute;
                break;
                . . .
        }
    }
}

… but an approach based on the PropertyChangeListener class is far better.

fantaghirocco
  • 4,761
  • 6
  • 38
  • 48
  • Oh, nice, I understand what you did here. I usually do something like this in C programming when I have to send complex data to threads. I think this should work, I'll give a try! – Andrea Efficace Sep 08 '20 at 09:47
  • it's not that nice but it's possible: yes, the class usage reminds a C struct somehow – fantaghirocco Sep 08 '20 at 09:50