Java MVC中无耦合模型间传递字符串的优化方案探讨
问题描述
我是Java新手,正在开发一款简易聊天程序,想要实现消息发送失败、输入无效等场景的弹窗提示。目前用观察者模式处理视觉更新,但需要在同属MVC架构Model层、必须保持解耦的ClientModel和GUIModel之间传递字符串。现在的实现用了多套观察者模式,感觉很冗余,想问问有没有更优的解决方案。
现有代码片段
Model类
public class Model implements ObservableMsg, ObservableSysMsg { private final ArrayList<Message> localHistory; private final String userID; private final ObservableMsgSupport obsMsg = new ObservableMsgSupport(); private final ObservableSysMsgSupport obsSMsg = new ObservableSysMsgSupport(); ... public void send(String msg) { try { Message newMsg = new Message(msg, this.userID, new Date()); appendMsg(newMsg); //This is temporary until backend is implemented obsMsg.updateMsg(); } catch (Exception e) { System.out.println("ERROR! Could not send msg."); //TODO Here is an example of where a popup message might be called to appear obsSMsg.updateSysMsg(); } } ... @Override public void addObserver(ObserverMsg o) { obsMsg.addObserver(o); } @Override public void removeObserver(ObserverMsg o) { obsMsg.removeObserver(o); } @Override public void addObserver(ObserverSysMsg o) { obsSMsg.addObserver(o); } @Override public void removeObserver(ObserverSysMsg o) { obsSMsg.removeObserver(o); } }
GUIModel类
public class GUIModel implements SystemMsgHandler, ObservableSysMsg { private final Set<String> systemMsgs; private final ObservableSysMsgSupport obs = new ObservableSysMsgSupport(); public GUIModel() { systemMsgs = new HashSet<String>(); } @Override public void addSystemMsg(String msg) { systemMsgs.add(msg); obs.updateSysMsg(); } @Override public void removeSystemMsg(String msg) { systemMsgs.remove(msg); obs.updateSysMsg(); } @Override public Set<String> getSystemMsgs() { return systemMsgs; } @Override public void addObserver(ObserverSysMsg o) { obs.addObserver(o); } @Override public void removeObserver(ObserverSysMsg o) { obs.addObserver(o); // 此处代码存在错误,应为removeObserver } }
GUIView类
public class GUIView implements ObserverMsg, ObserverSysMsg { ... @Override public void updateSysMsg() { popupPanel.removeAll(); viewModel.getSystemMsgs().forEach(errMsg -> popupPanel.add(new ErrorPopup(viewModel, errMsg).getContentPane())); this.frame.revalidate(); this.frame.repaint(); } }
优化方案
方案一:合并观察者体系,用事件类区分消息类型
不用分开定义多套观察者接口,统一成一套体系,通过自定义事件类区分普通消息和系统消息,减少冗余:
- 定义通用事件类和接口
// 通用事件类,携带事件类型和数据 public class ModelEvent { public enum Type { MESSAGE_UPDATED, SYSTEM_MESSAGE } private final Type type; private final Object data; public ModelEvent(Type type, Object data) { this.type = type; this.data = data; } public Type getType() { return type; } public Object getData() { return data; } } // 通用观察者接口 public interface Observer { void update(ModelEvent event); } // 通用被观察者接口 public interface Observable { void addObserver(Observer o); void removeObserver(Observer o); void notifyObservers(ModelEvent event); } // 通用观察者支持类 public class ObservableSupport implements Observable { private final List<Observer> observers = new ArrayList<>(); @Override public void addObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers(ModelEvent event) { for (Observer o : observers) { o.update(event); } } }
- 修改Model类,只用一套观察者
public class Model implements Observable { private final ArrayList<Message> localHistory; private final String userID; private final ObservableSupport observable = new ObservableSupport(); // ... 原有其他代码 public void send(String msg) { try { Message newMsg = new Message(msg, this.userID, new Date()); appendMsg(newMsg); // 发布普通消息更新事件 observable.notifyObservers(new ModelEvent(ModelEvent.Type.MESSAGE_UPDATED, newMsg)); } catch (Exception e) { String errorMsg = "消息发送失败:" + e.getMessage(); System.out.println(errorMsg); // 发布系统消息事件,直接传递错误文本 observable.notifyObservers(new ModelEvent(ModelEvent.Type.SYSTEM_MESSAGE, errorMsg)); } } @Override public void addObserver(Observer o) { observable.addObserver(o); } @Override public void removeObserver(Observer o) { observable.removeObserver(o); } }
- 修改GUIModel,作为观察者接收系统消息
public class GUIModel implements Observer, Observable { private final Set<String> systemMsgs; private final ObservableSupport observable = new ObservableSupport(); public GUIModel() { systemMsgs = new HashSet<>(); } @Override public void update(ModelEvent event) { if (event.getType() == ModelEvent.Type.SYSTEM_MESSAGE) { String msg = (String) event.getData(); systemMsgs.add(msg); // 通知视图更新系统消息列表 observable.notifyObservers(new ModelEvent(ModelEvent.Type.SYSTEM_MESSAGE, systemMsgs)); } } public void removeSystemMsg(String msg) { systemMsgs.remove(msg); observable.notifyObservers(new ModelEvent(ModelEvent.Type.SYSTEM_MESSAGE, systemMsgs)); } @Override public void addObserver(Observer o) { observable.addObserver(o); } @Override public void removeObserver(Observer o) { observable.removeObserver(o); } public Set<String> getSystemMsgs() { return systemMsgs; } }
- 修改GUIView处理事件
public class GUIView implements Observer { // ... 原有其他代码 @Override public void update(ModelEvent event) { switch (event.getType()) { case MESSAGE_UPDATED: // 此处编写普通聊天消息的更新逻辑,比如刷新聊天记录面板 break; case SYSTEM_MESSAGE: popupPanel.removeAll(); Set<String> errMsgs = (Set<String>) event.getData(); errMsgs.forEach(errMsg -> popupPanel.add(new ErrorPopup(viewModel, errMsg).getContentPane())); this.frame.revalidate(); this.frame.repaint(); break; } } }
方案二:轻量事件总线,彻底解耦组件
如果后续要扩展更多事件类型,可以自己实现一个简单的事件总线,让组件通过发布/订阅事件通信,无需互相持有引用:
- 实现事件总线
public class EventBus { private static final EventBus INSTANCE = new EventBus(); private final Map<Class<?>, List<EventListener<?>>> listeners = new HashMap<>(); private EventBus() {} public static EventBus getInstance() { return INSTANCE; } // 订阅事件 public <T> void subscribe(Class<T> eventType, EventListener<T> listener) { listeners.computeIfAbsent(eventType, k -> new ArrayList<>()).add(listener); } // 发布事件 public <T> void publish(T event) { List<EventListener<?>> eventListeners = listeners.get(event.getClass()); if (eventListeners != null) { for (EventListener<?> listener : eventListeners) { ((EventListener<T>) listener).onEvent(event); } } } // 事件监听接口 public interface EventListener<T> { void onEvent(T event); } }
- 定义具体事件类
// 普通消息发送事件 public class MessageSentEvent { private final Message message; public MessageSentEvent(Message message) { this.message = message; } public Message getMessage() { return message; } } // 系统提示事件 public class SystemTipEvent { private final String tip; public SystemTipEvent(String tip) { this.tip = tip; } public String getTip() { return tip; } }
- 修改Model发布事件
public void send(String msg) { try { Message newMsg = new Message(msg, this.userID, new Date()); appendMsg(newMsg); EventBus.getInstance().publish(new MessageSentEvent(newMsg)); } catch (Exception e) { String errorMsg = "消息发送失败:" + e.getMessage(); System.out.println(errorMsg); EventBus.getInstance().publish(new SystemTipEvent(errorMsg)); } }
- GUIModel订阅系统提示事件
public class GUIModel implements Observable { private final Set<String> systemMsgs; private final ObservableSupport observable = new ObservableSupport(); public GUIModel() { systemMsgs = new HashSet<>(); // 订阅系统提示事件 EventBus.getInstance().subscribe(SystemTipEvent.class, event -> { systemMsgs.add(event.getTip()); observable.notifyObservers(new ModelEvent(ModelEvent.Type.SYSTEM_MESSAGE, systemMsgs)); }); } // ... 其他方法不变 }
方案三:最小改动现有代码
如果不想重构整个观察者体系,直接修改现有接口,让系统消息的观察者直接传递内容,去掉中间空触发的冗余:
- 修改ObservableSysMsg和ObserverSysMsg接口
public interface ObservableSysMsg { void addObserver(ObserverSysMsg o); void removeObserver(ObserverSysMsg o); void notifySysMsg(String msg); // 带参数,直接传递消息内容 } public interface ObserverSysMsg { void updateSysMsg(String msg); // 接收消息内容 }
- 修改ObservableSysMsgSupport实现
public class ObservableSysMsgSupport implements ObservableSysMsg { private final List<ObserverSysMsg> observers = new ArrayList<>(); @Override public void addObserver(ObserverSysMsg o) { observers.add(o); } @Override public void removeObserver(ObserverSysMsg o) { observers.remove(o); } @Override public void notifySysMsg(String msg) { for (ObserverSysMsg o : observers) { o.updateSysMsg(msg); } } }
- 修改Model的send方法
public void send(String msg) { try { // ... 原有逻辑 } catch (Exception e) { String errorMsg = "消息发送失败:" + e.getMessage(); System.out.println(errorMsg); obsSMsg.notifySysMsg(errorMsg); // 直接传递错误消息 } }
- 修改GUIModel实现ObserverSysMsg
public class GUIModel implements ObserverSysMsg, ObservableSysMsg { private final Set<String> systemMsgs; private final ObservableSysMsgSupport obs = new ObservableSysMsgSupport(); public GUIModel() { systemMsgs = new HashSet<>(); } @Override public void updateSysMsg(String msg) { systemMsgs.add(msg); obs.notifySysMsg(msg); // 或者直接通知视图,根据需求调整 } // ... 其他方法不变,修正removeObserver的错误 @Override public void removeObserver(ObserverSysMsg o) { obs.removeObserver(o); } }
内容的提问来源于stack exchange,提问作者flurp.potatis
相关产品推荐
相关产品推荐

