【问题标题】:How to find type without using instanceof?如何在不使用 instanceof 的情况下查找类型?
【发布时间】:2018-07-08 17:16:27
【问题描述】:

我的班级Query 中有一个接口类型为CriteriaList

List<Criteria> criteria = new ArrayList<Criteria>();

我有几个Criteria 的具体实现。我想给Query 一个迭代我的criteria 列表的方法,并根据具体类型执行一些逻辑。

我目前正在使用instanceof 这样做:

for(Criteria c : criteria) {
    if(c instanceof ContextualCriteria){
        // logic
    }
    ...
}

这是唯一/最好的方法吗?

【问题讨论】:

  • 也许您可以在Criteria 中定义行为并让对象执行逻辑?

标签: java instanceof


【解决方案1】:

逻辑是否明显属于Criteria 本身?如果是这样,请将其放入Criteria 接口并为实现Criteria 接口的每个具体类适当地实现它。这显然是 nice 多态方法。

不幸的是,在现实生活中,OO 并不总是那么简单 - 有时将每个类型的行为放在类型本身是没有意义的,因此您可能需要使用 instanceof 代替。您可能可能拥有从“标准类”到表示要采取的操作的某个接口的映射,但这很容易最终变得更加混乱。

通过visitor pattern 进行双重调度有时 可以稍微改进一下——因此逻辑仍然可以在您的“调用”类中的单独方法中,但每个Criteria 都可以调用返回通过单一接口方法返回到正确的方法。就我个人而言,我倾向于发现这会增加耦合并很快变得丑陋,但其他人对此深信不疑。

【讨论】:

  • 不幸的是,逻辑不适合Criteria。我同意,您提供的两个替代方案不太简洁。
  • instanceof 周围有什么污名?
  • @wulfgar.pro:在我看来,这有点教条——忽略了生活并不总是以我们可能喜欢的最面向对象的方式进行的事实。多态性在可能的情况下是可取的,但它并不总是有帮助。
【解决方案2】:

如果逻辑不属于 Jon Skeet 建议的标准,那么您可以使用 visitor pattern

在具体标准中:

public void accept(CriteriaVisitor v) {
    v.visit(this);
}

在客户端代码中:

public void method() {
    for (Criteria c : criteria) {
        c.accept(this);
    }
}

public void visit(ConcreteCriteria c) {
    // do logic here
}

public void visit(Criteria c) {
    // othervise...
}

这里去掉了instanceof,但要小心,我发现如果你不熟悉代码,这种模式很难理解。

【讨论】:

    【解决方案3】:

    嗯,你可以...

    if (ContextualCriteria.class.equals(c.getClass()) {
    

    ...虽然这只是instanceof 的一种更漂亮的写作方式。 (嗯,差不多:这测试它是否完全是类,而不是子类的类——因为你想要isAssignableFrom())。

    消除异味的正确方法是在Criteria 中实现一个多态方法,例如,该方法在子类中被覆盖。

    【讨论】:

    • 正如我在对忽必烈的评论中所说:不,这与 instanceof 不同。你不会匹配任何子类
    • 对,这就是我在回复中所说的。他想根据特定的类执行逻辑。实际上,基于类或子类执行可能是错误的——当您的意思是 A 时,您可能会在 A 的子句中匹配 B 类。我的回答是有意的;我不认为你标记它的理由是有效的。
    • @Sean Owen - 多态方式是我最初的实现方式 - 然而,Query 中的逻辑不适合 Criteria 实现 =(.
    【解决方案4】:

    界面是策略模式的一半!要根据类型改变逻辑,尽可能将其推到接口后面,这样 Criteria 就有一个 doLogic()。您可以通过该方法传递您可能需要在调用代码中更改的任何参数,或返回新信息 - 这是非常具体的实现,很难从相关代码中获得建议。

    如果一切顺利,你的调用代码就结束了

    for (Criteria c : criteria) {
        c.doLogic();
    }
    

    【讨论】:

      【解决方案5】:

      您还可以在实现 Criteria 的类中实现逻辑

      public interface Criteria {
        public void logic();
      }
      

      在 ContextualCriteria 等类中有几个实现 你的循环看起来很干净:

      for(Criteria c : criteria) {  
         c.logic();
      }  
      

      【讨论】:

        【解决方案6】:

        你也可以使用:

        for(Criteria c : criteria) {
           if(c.getClass() == ContextualCriteria.class){
               // logic
           }
           if ...
        }
        

        注意Object#getClass() 返回c 的运行时类型,所以如果ContextualCriteria 可以被子类化,你就不能可靠地使用它。为此,您需要使用Class#isAssignableFrom():

        for(Criteria c : criteria) {
           if(ContextualCriteria.class.isAssignableFrom(c)){
               // logic
           }
           if ...
        }
        

        【讨论】:

        • 这是更好的方法吗?为什么?
        • 这不一样:你的测试只会匹配这个特定的类,而instanceof也会匹配子类。
        • @wulfgar - 这不一定更好;我只是根据您的问题展示其他方式。就有意义的讨论而言,Jon Skeet 的回答是最好的。
        【解决方案7】:

        没有错。

        从未使用过instanceof 的人只会编写玩具应用程序。

        【讨论】:

          猜你喜欢
          • 2016-05-18
          • 1970-01-01
          • 2017-05-31
          • 1970-01-01
          • 1970-01-01
          • 2020-09-03
          • 1970-01-01
          • 2017-08-25
          相关资源
          最近更新 更多