【问题标题】:Java - Behavioral decisions "instanceof" vs flagJava - 行为决策“instanceof”与标志
【发布时间】:2018-05-02 10:49:29
【问题描述】:

我目前正在编写一段代码,其中根据请求对象是否属于某个子类型来做出行为决策,如下所示:

class Request{
    public void doThing(){
        System.out.println("doing Thing...");
    }
}

class SpecialRequest extends Request{
    ...
}

void method(Request request){
    if(request instanceof SpecialRequest){
        System.out.println("is special...");
    }
    request.doThing();
}

在这种情况下,SpecialRequest 没有其他任何实现,这将其与普通请求区分开来。我现在的问题是,使用这种风格是否有任何优点或缺点:

class Request{
    ...

    public boolean isSpecial(){
        return special;
    }

    public void doThing(){
        System.out.println("doing Thing...");
    }
}

void method(Request request){
    if(request.isSpecial()){
        System.out.println("is special...");
    }
    request.doThing();
}

我了解第二种方法如何变得非常复杂,因为我想做的决定越多。但我也对这两种方法在性能方面的比较感兴趣。

提前致谢。

编辑:首先感谢您的快速回复。我可能应该提到,我的 Request 和我的 SpecialRequest 应该只是携带数据,而不包含任何逻辑。这就是整个软件的设计方式。我的方法只是消耗了请求,并且应该根据请求是否特殊而表现不同。 “instanceof”对我来说似乎是脏代码,但布尔值似乎也不太正确。仍然欢迎任何建议。

【问题讨论】:

  • 行为差异应该在Reqeuest类的重写方法中,而不是使用instanceof
  • instanceof 通常是代码异味。布尔值也没有解决问题并增加了耦合。我不知道您的其余逻辑,但考虑在基类中实现一个方法,以便子类可以根据它们的类型完成工作。而且你不应该担心性能,这是过早的优化。
  • 感谢您的回答,问题是差异不在于请求,而在于使用请求的方法。或者您是否建议覆盖“isSpecial ()”并让它在基本请求中返回 false。

标签: java performance oop instanceof


【解决方案1】:

使用方法的明显优势在于它与特定类型没有紧密联系。您可以添加一个新类 VerySpecialRequest,它也提供此行为,并且您不一定需要扩展 SpecialRequest 才能做到这一点。


但是,这里更紧迫的问题是您完全有明确的条件分支。对象应该负责确定是否需要任何额外的行为,然后讨厌的条件逻辑就完全消失了:

class Request{
    public void doThing(){
        System.out.println("doing Thing...");
    }
}

class SpecialRequest extends Request{
    @Override
    public void doThing(){
        super.doThing();
        System.out.println("is special...");
    }
}

【讨论】:

    【解决方案2】:

    您也可以考虑使用访问者模式 (https://en.wikipedia.org/wiki/Visitor_pattern),因为它可以确保类型安全并且不需要实例检查。此外,当您创建一种新类型的请求(例如“VerySpecialRequest”)时,您必须扩展/修改代码的地方更加明显。

    您所要做的就是为您想要执行特殊代码的每种类型创建一个带有访问方法的接口,如下所示:

    public interface RequestProcessor {
      void visit(Request request);
      void visit(SpecialRequest specialRequest);
    }
    

    请求类需要接受方法,除了自己调用访问者之外什么都不做:

    public class Request {
    
      ...
    
      void accept(RequestProcessor requestProcessor) {
        requestProcessor.visit(this);
      }
    }
    

    特殊要求也一样:

    public class SpecialRequest extends Request {
    
      ...
    
      @Override
      void accept(RequestProcessor requestProcessor) {
        requestProcessor.visit(this);
      }
    }
    

    然后你可以简单地在一个实现接口的类中实现你的逻辑(这里是一个匿名类):

    RequestProcessor requestProcessor = new RequestProcessor() {
    
      @Override
      public void visit(Request request) {
        System.out.println("I'm a normal request");
      }
    
      @Override
      public void visit(SpecialRequest specialRequest) {
        System.out.println("I'm a special request");
      }
    };
    
    
    Request request = new Request();
    request.accept(requestProcessor);
    
    SpecialRequest specialRequest = new SpecialRequest();
    specialRequest.accept(requestProcessor);
    

    如您所见,不再有实例检查。这样做的另一个优点是即使它们不是“请求”的直接子类,您也可以“处理请求” - 您所要做的就是在其自己的接口中提取接受方法。希望这会有所帮助。

    【讨论】:

    • 这实际上对我来说似乎是最好的方法。它非常适合我已经拥有的代码,它使整个事情变得更加清晰。非常感谢。
    【解决方案3】:

    1.- 而不是,是一个类型比较运算符,因为它将实例与类型进行比较。它返回真或假。如果将 instanceof 运算符应用于任何具有 null 值的变量,它将返回 false。

    2.- isSpecial(),是你自己的方法。你可以在那段代码中做任何你想做的事情,所以在这种情况下的比较取决于你。

    3.- 你必须知道,当你使用 instanceof 时,比较也会对提到的变量进行类转换,所以如果不能这样做,你应该得到一个编译时错误。看到这个:compile time error with instanceof

    4.- 如果你经常使用这个操作符,你应该检查它,因为这是一些错误代码的信号。更多详情请看:use of instaceof

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-18
      • 2014-11-23
      • 1970-01-01
      • 2019-07-22
      • 2013-06-18
      • 2014-05-22
      • 2011-03-05
      相关资源
      最近更新 更多