如何使用观察者模式观察相同类型的不同属性?

huangapple go评论79阅读模式
英文:

How to observe different attributes of same types with the Observer pattern?

问题

我在MVC应用程序中实现了观察者模式,以便一个Bean可以监听模型中的更改。由于我使用的是Java 8,所以我使用了Java.util中的Observer和Observable。以下是我编写的部分代码:

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;
        // 如何监听此更改?
        /*setChanged();
        notifyObservers(email);*/
    }
}

我认为这个实现是正确的,因为在我进行的一个简单测试中,对于Id和Name的更改都被Bean类正确地读取了。但我意识到,我无法监听电子邮件属性的更改,因为在Bean类中我正在使用“instanceof”来理解我需要更新哪个值。

有没有一种方法可以识别哪个变量已经改变了?我应该实现自己的Observer和Observable类,而不是使用Java.Util吗?
我认为通过第二种解决方案,我可以为每个属性定义一个update方法,但是我不知道如何在Java中管理同步过程。

英文:

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.

答案1

得分: 2

update 方法中的第一个参数是一个 Observable。那是您的 UserModel 对象已更改的对象,因此它包含了所有已更新的数据。因此,您可以使用第二个参数来传递对象的新 ,也可以使用它来传递已更改的对象的 名称(或者使用一个 enum,因为这样更加 清晰)。

一个解决方案可能如下所示:

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;
    /* 获取器和设置器 */
    
    @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("意外的 ChangedValue 类型: " + 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); // 在这里使用枚举类型作为参数
    }
    
    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers(ChangedValue.NAME);
    }
    
    public void setEmail(String email) {
        this.name = email;
        setChanged();
        notifyObservers(ChangedValue.EMAIL);
    }
}

注: 如评论中所述,通用方法会更好,以避免对象转换。可以类似于这个实现来使用它。只需添加一些通用参数,但思想保持不变。

英文:

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);
	}
}

<hr>

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.

答案2

得分: 2

我指的是这样的:

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));
    }
    // ...
}

观察者:

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;
            // ...
        }
    }
}

... 但基于 PropertyChangeListener 类的方法要好得多。

英文:

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.

huangapple
  • 本文由 发表于 2020年9月8日 17:12:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/63790775.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定