首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我的EventBus系统后续行动

我的EventBus系统后续行动
EN

Code Review用户
提问于 2014-05-05 11:37:25
回答 1查看 173关注 0票数 6

这个问题是我上一个问题我的EventBus系统的后续问题,它包含了@rolfl的回答的大部分要点。

它包括但不限于:

  • 在普通方法上使用Collections.synchronizedSet优于手动synchronized { }
  • 最小锁定
  • 高性能代码,但如果它损害了代码的可读性,则不会进行微优化。

守则:

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Event { }
代码语言:javascript
复制
public interface EventBus {
    void registerListenersOfObject(final Object callbackObject);

    <T> void registerListener(final Class<T> eventClass, final Consumer<? extends T> eventListener);

    void executeEvent(final Object event);

    void removeListenersOfObject(final Object callbackObject);

    <T> void removeListener(final Class<T> eventClass, final Consumer<? extends T> eventListener);

    void removeAllListenersOfEvent(final Class<?> eventClass);

    void removeAllListeners();
}
代码语言:javascript
复制
public class SimpleEventBus implements EventBus {
    private final static Set<EventHandler> EMPTY_SET = new HashSet<>();
    private final static EventHandler[] EMPTY_ARRAY = new EventHandler[0];

    private final ConcurrentMap<Class<?>, Set<EventHandler>> eventMapping = new ConcurrentHashMap<>();
    private final Class<?> eventClassConstraint;

    public SimpleEventBus() {
        this(Object.class);
    }

    public SimpleEventBus(final Class<?> eventClassConstraint) {
        this.eventClassConstraint = Objects.requireNonNull(eventClassConstraint);
    }

    @Override
    public void registerListenersOfObject(final Object callbackObject) {
        Arrays.stream(callbackObject.getClass().getMethods())
                .filter(this::isEligibleMethod)
                .forEach(method -> {
                    Class<?> eventClass = method.getParameterTypes()[0];
                    addEventHandler(eventClass, new MethodEventHandler(method, callbackObject, eventClass));
                });
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> void registerListener(final Class<T> eventClass, final Consumer<? extends T> eventListener) {
        Objects.requireNonNull(eventClass);
        Objects.requireNonNull(eventListener);
        if (eventClassConstraint.isAssignableFrom(eventClass)) {
            addEventHandler(eventClass, new ConsumerEventHandler((Consumer<Object>)eventListener));
        }
    }

    @Override
    public void executeEvent(final Object event) {
        if (eventClassConstraint.isAssignableFrom(event.getClass())) {
            getCopyOfEventHandlers(event.getClass()).forEach(eventHandler -> eventHandler.invoke(event));
        }
    }

    @Override
    public void removeListenersOfObject(final Object callbackObject) {
        Arrays.stream(callbackObject.getClass().getMethods())
                .filter(this::isEligibleMethod)
                .forEach(method -> {
                    Class<?> eventClass = method.getParameterTypes()[0];
                    removeEventHandler(eventClass, new MethodEventHandler(method, callbackObject, eventClass));
                });
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> void removeListener(final Class<T> eventClass, final Consumer<? extends T> eventListener) {
        Objects.requireNonNull(eventClass);
        Objects.requireNonNull(eventListener);
        if (eventClassConstraint.isAssignableFrom(eventClass)) {
            removeEventHandler(eventClass, new ConsumerEventHandler((Consumer<Object>)eventListener));
        }
    }

    @Override
    public void removeAllListenersOfEvent(final Class<?> eventClass) {
        eventMapping.remove(Objects.requireNonNull(eventClass));
    }

    @Override
    public void removeAllListeners() {
        eventMapping.clear();
    }

    private boolean isEligibleMethod(final Method method) {
        return (method.getAnnotation(Event.class) != null
                && method.getReturnType().equals(void.class)
                && method.getParameterCount() == 1
                && eventClassConstraint.isAssignableFrom(method.getParameterTypes()[0]));
    }

    private void addEventHandler(final Class<?> eventClass, final EventHandler eventHandler) {
        Objects.requireNonNull(eventClass);
        Objects.requireNonNull(eventHandler);
        eventMapping.putIfAbsent(eventClass, Collections.synchronizedSet(new HashSet<>()));
        eventMapping.get(eventClass).add(eventHandler);
    }

    private void removeEventHandler(final Class<?> eventClass, final EventHandler eventHandler) {
        Objects.requireNonNull(eventClass);
        Objects.requireNonNull(eventHandler);
        eventMapping.getOrDefault(eventClass, EMPTY_SET).remove(eventHandler);
    }

    private Stream<EventHandler> getCopyOfEventHandlers(final Class<?> eventClass) {
        Set<EventHandler> eventHandlers = eventMapping.get(Objects.requireNonNull(eventClass));
        return (eventHandlers == null) 
                ? Stream.empty()
                : Arrays.stream(eventHandlers.toArray(EMPTY_ARRAY));
    }

    private static interface EventHandler {
        void invoke(final Object event);
    }

    private static class MethodEventHandler implements EventHandler {
        private final Method method;
        private final Object callbackObject;
        private final Class<?> eventClass;

        public MethodEventHandler(final Method method, final Object object, final Class<?> eventClass) {
            this.method = Objects.requireNonNull(method);
            this.callbackObject = Objects.requireNonNull(object);
            this.eventClass = Objects.requireNonNull(eventClass);
        }

        @Override
        public void invoke(final Object event) {
            try {
                method.setAccessible(true);
                method.invoke(callbackObject, Objects.requireNonNull(event));
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                throw new RuntimeException(ex);
            }
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 71 * hash + Objects.hashCode(this.method);
            hash = 71 * hash + Objects.hashCode(this.callbackObject);
            hash = 71 * hash + Objects.hashCode(this.eventClass);
            return hash;
        }

        @Override
        public boolean equals(final Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final MethodEventHandler other = (MethodEventHandler)obj;
            if (!Objects.equals(this.method, other.method)) {
                return false;
            }
            if (!Objects.equals(this.callbackObject, other.callbackObject)) {
                return false;
            }
            if (!Objects.equals(this.eventClass, other.eventClass)) {
                return false;
            }
            return true;
        }
    }

    private static class ConsumerEventHandler implements EventHandler {
        private final Consumer<Object> eventListener;

        public ConsumerEventHandler(final Consumer<Object> consumer) {
            this.eventListener = Objects.requireNonNull(consumer);
        }

        @Override
        public void invoke(final Object event) {
            eventListener.accept(Objects.requireNonNull(event));
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 19 * hash + Objects.hashCode(this.eventListener);
            return hash;
        }

        @Override
        public boolean equals(final Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final ConsumerEventHandler other = (ConsumerEventHandler)obj;
            if (!Objects.equals(this.eventListener, other.eventListener)) {
                return false;
            }
            return true;
        }
    }
}

旧版本的单元测试仍然有效,并且仍然通过。

EN

回答 1

Code Review用户

发布于 2014-05-05 22:29:50

虽然同步现在是正确的,但Collections.synchronizedSet(new HashSet<>()))仍然是一种相当粗粒度的同步方式。您可以用Collections.newSetFromMap(new ConcurrentHashMap<>())替换它,以便从ConcurrentHashMap的更好的锁定策略中获益。

使用Map.computeIfAbsent()在Java8中使用lambdas可以更容易地从Map中获取值,或者在不存在的情况下输入一个值,您可以替换:

代码语言:javascript
复制
eventMapping.putIfAbsent(eventClass, Collections.newSetFromMap(new ConcurrentHashMap<>()));
eventMapping.get(eventClass).add(eventHandler);

通过

代码语言:javascript
复制
eventMapping.computeIfAbsent(eventClass, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(eventHandler);
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/48970

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档