避免强制转换数据实体的Java设计模式

时间:2018-11-29 09:57:50

标签: java design-patterns

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

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

它看起来像这样(我省略了构造函数-设置所有字段和getters / setters-它们只是正常的默认值):

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

我不喜欢这种强制转换-感觉我在获取事件时无法控制类型,而且我看到这种情况像“弱点”和错误生成器。是否有任何设计模式可以避免这种情况,还是应该重新设计整个系统?也许这是唯一的方法,我不在乎

1 个答案:

答案 0 :(得分:1)

我建议您使用访客模式以避免演员表转换。你是对的,应谨慎使用。

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

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