首页 > 解决方案 > 什么java设计模式可以避免强制转换数据实体

问题描述

我目前正在我的应用程序中实现类似异步的事件队列 - 它被设计为这样工作:一个组件对一些用户输入做出反应并将事件放入队列,另一个“侦听器”正在检查是否存在特定类型的事件在队列中运行它自己的业务逻辑

可以有各种事件类型(如 USER_MOUSE_CLICK、USER_KEYBOARD_CLICK 等),每个实现都有自己的“事件对象”类型

它看起来像这样(我正在省略构造函数 - 设置所有字段和 getter/setter - 它们只是普通的默认值):

public abstract MyEvent<T> {
    private EventType eventType;
    private T eventData;
}

public MouseClickEvent extends MyEvent<ClickPoint> { // ClickPoint class contains x,y of mouse click
    public MouseClick(ClickPoint point) {
        super(EventType.USER_MOUSE_CLICK, point);
    }
}

public KeyboardClickEvent extends MyEvent<String> { // character that has been clicked on keyboard
    public MouseClick(String key) {
        super(EventType.USER_KEYBOARD_CLICK, key);
    }
}

我还有一个带有MyEvent实例队列的服务以及检索提供的第一个事件(EventType如果存在)的方法 - 它看起来像

...
private List<MyEvent> queue;
...
public MyEvent fetchMyEvent(EventType eventType) {
    for(MyEvent event : queue) {
        if(event.getEventType().equals(eventType) {
            return event;
        }
    }
    return null;
}
...

问题是,当我尝试检索事件时,我需要将其转换为特定的实现,例如

// some listener logic
MouseClickEvent event = (MouseClickEvent ) eventService.fetchMyEvent(EventType.USER_MOUSE_CLICK);
log("The X point of mouse click was: " + event.getEventData().x);

我不喜欢这种强制转换——我觉得当我获取事件时我无法控制类型,我认为这种情况就像一个“弱点”和错误生成器。是否有任何设计模式可以避免这种情况,或者我应该重新设计整个系统?或者也许这是唯一的方法,我不应该关心

标签: javadesign-patterns

解决方案


我建议您使用访客模式以避免强制转换。你是对的,铸造应该谨慎使用。

要使用访问者模式,您必须:首先:定义访问者界面:

public interface IEventVisitor {

    void visit(MouseClickEvent event);

    void visit(KeyboardClickEvent event);

}

第二个在你的晚餐类中定义一个抽象方法接受:

public abstract MyEvent<T> {
    private EventType eventType;
    private T eventData;

    public abstract void accept(IEventVisitor visitor);
}

第三:将每个子类的方法实现为:

public MouseClickEvent extends MyEvent<ClickPoint> { // ClickPoint class contains x,y of mouse click
    public MouseClick(ClickPoint point) {
        super(EventType.USER_MOUSE_CLICK, point);
    }

    public void accept(IEventVisitor visitor) {
        visitor.visit(this);

    }
}

最后实现您的访问者界面。

使用将如下所示,实例化访问者并调用 event.accept(visitor)。

Event event = eventService.fetchMyEvent(EventType.USER_MOUSE_CLICK);
IEventVisitor visitor = new IEventVisitor() {

            @Override
            public void visit(MouseClickEvent event) {
                // logic goes here
            }

            @Override
            public void visit(KeyboardClickEvent event) {
                // logic goes here
            }
        };


event.accept(visitor);

推荐阅读