【问题标题】:Is there a better way to short-circuit a chain of instanceof?有没有更好的方法来短路实例链?
【发布时间】:2017-02-04 06:46:59
【问题描述】:

我的课程最初从几个单独的 addAListener() addBListener()removeAListener 等开始。这对一个课程来说还不错,但是当该课程被另一个课程在内部使用并且扩展的听众得到传播出去。

一个简单的解决方案是使用单个接口并使用instanceof 来整理监听器:

public interface Listener {
}

public class ListenerA extends Listener {
}

public class ListenerB extends Listener {
}

public class ListenerC extends Listener {
}

List<ListenerA> ofListenersA = new List<>();
List<ListenerB> ofListenersB = new List<>();
List<ListenerC> ofListenersC = new List<>();

void addListener(Listener listener) {
    if (listener instanceof ListenerA) {
        ofListenersA.add(listener);

        return;
    }

    if (listener instanceof ListenerB) {
        ofListenersB.add(listener);

        return;
    }

    if (listener instanceof ListenerB) {
        ofListenersB.add(listener);

        return;
    }
}

void removeListener(Listener listener) {
    if (listener instanceof ListenerA) {
        ofListenersA.remove(listener);

        return;
    }

    if (listener instanceof ListenerB) {
        ofListenersB.remove(listener);

        return;
    }

    if (listener instanceof ListenerB) {
        ofListenersB.remove(listener);

        return;
    }
}

但现在我必须专门评估每个 instanceof,因为你不能在课堂上 switch

这不是要求优化的尝试,因为我没有多种侦听器需要检查;而是一个问题,当涉及到面向对象的设计时,这是否是一种糟糕的方法。

更新

在接口中使用枚举的短路方法:

enum ListenerType {
    ListenerTypeA,
    ListenerTypeB,
    ListenerTypeC
}

public interface Listener {
    ListenerType getType();
}

public class ListenerA extends Listener {
    ListenerType getType() {
        return ListenerType.ListenerTypeA;
    }
}

public class ListenerB extends Listener {
    ListenerType getType() {
        return ListenerType.ListenerTypeB;
    }
}

public class ListenerC extends Listener {
    ListenerType getType() {
        return ListenerType.ListenerTypeC;
    }
}

List<ListenerA> ofListenersA = new List<>();
List<ListenerB> ofListenersB = new List<>();
List<ListenerC> ofListenersC = new List<>();

void addListener(Listener listener) {
    switch (listener) {
        case ListenerTypeA: {
            ofListenersA.add(listener);

            return;
        }
        case ListenerTypeB: {
            ofListenersB.add(listener);

            return;
        }
        case ListenerTypeC: {
            ofListenersC.add(listener);

            return;
        }
    }
}

void removeListener(Listener listener) {
    switch (listener) {
        case ListenerTypeA: {
            ofListenersA.remove(listener);

            return;
        }
        case ListenerTypeB: {
            ofListenersB.remove(listener);

            return;
        }
        case ListenerTypeC: {
            ofListenersC.remove(listener);

            return;
        }
    }
}

【问题讨论】:

  • 如果您使用的是instanceof,那么您的设计可能是错误的。为什么要单独跟踪所有这些侦听器,如果需要将它们分开,为什么要使用单个 addListener 方法?
  • 因为我有其他对象使用此类的对象作为私有成员。因此,当我想从上面提供侦听器时,我必须再次为该类重新实现每个添加/删除侦听器。我将它们都作为单独的侦听器的原因是因为我为不同的事件单独循环它们中的每一个。
  • 我查看了很多开源项目,但我不记得有谁使用单一方法作为通用侦听器,通常:您可能希望为每个侦听器创建一个方法并添加一些直观的名称。跨度>
  • @Zhro A class 不能扩展 interface 。 #justsaying。
  • @Zhro,你可以有一个接口,在它的抽象方法中你可以有一个定义事件类型的变量(变量可以是枚举类型)。现在是单一接口和单一方法。您还需要单个侦听器数组。另一种方法是基于事件类型的单个接口,其中包含各种方法。只要注意你最终不会有胖接口。无论如何,太多分散的代码也不好。请参阅下面我的回答。希望它会是hepful

标签: java oop switch-statement instanceof


【解决方案1】:

我建议您添加一个类型来指定您感兴趣的侦听器类型。您还可以将键更改为其他内容,例如带有 hashCode 和 equals 的常规类。

enum ListenerType {
    TYPE_A, TYPE_B, TYPE_C
}

interface Listener {

}

Map<ListenerType, Set<Listener>> listeners = new ConcurrentHashMap<>();

public void addListener(ListenerType type, Listener listener) {
    listeners.computeIfAbsent(type, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(listener);
}

public void removeListener(ListenerType type, Listener listener) {
    listeners.computeIfPresent(type, (k, v) -> v.remove(listener) && v.isEmpty() ? null : v);
}

public Set<Listener> getListeners(ListenerType type) {
    return listeners.getOrDefault(type, Collections.emptySet());
}

【讨论】:

    【解决方案2】:

    (根据我的经验)总有比在不了解您的代码库的情况下使用 instanceOf 更好的方法,很难说什么是最合适的,但根据情况,以下是类型比较的好方法:

    • 映射,它们的操作方式与基于类型的 switch 语句大致相同,只要您将映射的键设置为您希望打开的类型,然后由您决定何时返回什么它匹配。

    • Command Pattern,您将侦听器类层次结构替换为抽象/具体命令。然后您可以将实际行为封装到接收器中。

    • Visitor Pattern,如果您想一次通知所有侦听器,那么访问者模式可能适用,类型比较将通过您的侦听器层次结构再次完成,作为被访问的元素。

      李>

    我希望这有点用,如果我不在基地,请告诉我。

    【讨论】:

      【解决方案3】:

      我们在这种情况下使用了访问者模式。 在添加新的监听器实现时,使用访问者模式维护代码也很容易。

      【讨论】:

        【解决方案4】:

        有一个interface 并定义一个enum。在interfaceabstract method 中,您可以将enum 的实例作为参数。

        现在您只需要一个监听器列表。

        现在您可以根据动作或事件类型调用方法。在实施时,您可以按照enum instance type 处理。

        另一种方法是使用single interfacedifferent methods。根据发生的事件调用方法,在实现时可以根据方法进行处理。

        使用定义事件类型的变量(枚举、字符串或整数类型的变量,但更喜欢枚举)的单一方法方法的优点是,如果添加更多操作,您只需要在界面中添加这些操作,而不需要添加太多将需要改变。您只需要在枚举中添加添加的操作(如果您有字符串或整数常量,那么您可以相应地为新操作定义额外的常量)。现在您可以使用新定义的操作类型调用相同的方法。

        只需要注意避免Fat 接口。您需要在太瘦(每个方法单个接口)与胖接口之间取得平衡。您可以将属于接口中逻辑相同任务的方法分组。过于分散的代码也很糟糕,将每个(相关和不相关的)api 放在一个接口中也很糟糕。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-08-19
          • 2012-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-23
          • 2013-12-12
          • 2011-12-20
          相关资源
          最近更新 更多