You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    }
}
优化方案

方案一:合并观察者体系,用事件类区分消息类型

不用分开定义多套观察者接口,统一成一套体系,通过自定义事件类区分普通消息和系统消息,减少冗余:

  1. 定义通用事件类和接口
// 通用事件类,携带事件类型和数据
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);
        }
    }
}
  1. 修改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); }
}
  1. 修改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; }
}
  1. 修改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;
        }
    }
}

方案二:轻量事件总线,彻底解耦组件

如果后续要扩展更多事件类型,可以自己实现一个简单的事件总线,让组件通过发布/订阅事件通信,无需互相持有引用:

  1. 实现事件总线
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);
    }
}
  1. 定义具体事件类
// 普通消息发送事件
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; }
}
  1. 修改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));
    }
}
  1. 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));
        });
    }

    // ... 其他方法不变
}

方案三:最小改动现有代码

如果不想重构整个观察者体系,直接修改现有接口,让系统消息的观察者直接传递内容,去掉中间空触发的冗余:

  1. 修改ObservableSysMsg和ObserverSysMsg接口
public interface ObservableSysMsg {
    void addObserver(ObserverSysMsg o);
    void removeObserver(ObserverSysMsg o);
    void notifySysMsg(String msg); // 带参数,直接传递消息内容
}

public interface ObserverSysMsg {
    void updateSysMsg(String msg); // 接收消息内容
}
  1. 修改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);
        }
    }
}
  1. 修改Model的send方法
public void send(String msg) {
    try {
        // ... 原有逻辑
    } catch (Exception e) {
        String errorMsg = "消息发送失败:" + e.getMessage();
        System.out.println(errorMsg);
        obsSMsg.notifySysMsg(errorMsg); // 直接传递错误消息
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 14:55:00