【问题标题】:Java code refactoring: multiple instanceof operator usagesJava 代码重构:多个 instanceof 运算符的用法
【发布时间】:2014-09-25 14:35:35
【问题描述】:

考虑以下示例,其中类 TextFileXmlFileHtmlFileShellScriptFile 等都是类 SimpleFile 的子类。我正在编写一个类FileOperations,它有一个根据类型搜索通用文件内容的方法。

以下是代码示例:

public searchFile(SimpleFile targetFile, String searchStr) {
     if (targetPage instanceof HtmlFile) {
         // search html file
     }
     else if (targetPage instanceof TextFile) {
         // search text file
     }
     else if (targetPage instanceof XmlFile) {
         // search xml file
     }
     else if (targetPage instanceof ShellScriptFile) {
         // search shell file
     }   
     ...
}

这个结构对我来说很难闻。我知道这是多态性的最佳情况。但我无法控制 File 类或其子类。我无法给它们写信。

还有其他方法可以清理这个烂摊子吗?因为随着我添加对不同文件类型的支持,if-else 结构会不断增加。

否则,如果我坚持这个结构,那么instanceof 是 Java 中最快的运算符吗?它对性能有何影响?

对于上述情况,使用getClass()isAssignableFrom() 会更好吗?

感谢您的 cmets 或建议!

【问题讨论】:

  • 在这种情况下速度无关紧要
  • 因为,我猜“我无法控制文件类或其子类”
  • 正如我所说,我不能写给超类或子类。我无法控制它们。

标签: java refactoring polymorphism operators instanceof


【解决方案1】:

首先我想说你的例子看起来一点也不坏。这有点冗长,并且可以说不如将它们分成不同的方法那样具有凝聚力,但对我来说看起来很正常。我认为更简洁的方法可能是使用重载。我不能说速度上的差异,但从维护和可扩展性的角度来看,将来会更容易处理。

public void searchFile(SimpleFile targetFile , String searchStr) {
     // SimpleFile generalized behavior.
     if (targetPage instanceof HtmlFile) searchFile((HtmlFile)targetFile, searchStr);
     else if (targetPage instanceof TextFile) searchFile((TextFile)targetFile, searchStr);
     else if (targetPage instanceof XmlFile) searchFile((XmlFile)targetFile, searchStr);
     else if (targetPage instanceof ShellScriptFile) searchFile((ShellScriptFile)targetFile, searchStr);
     else System.out.println("Subtype not recognised"); 
}
public void searchFile(HtmlFile targetFile , String searchStr) {
    // HtmlFile specific behavior
}
public void searchFile(TextFile targetFile , String searchStr) {
    // TextFile specific behavior
}
public void searchFile(XmlFile targetFile , String searchStr) {
    // XmlFile specific behavior
}
public void searchFile(ShellScriptFile targetFile , String searchStr) {
    // ShellScript specific behavior
}

如果有SimpleFile 的新子类,它将默认为SimpleFile 版本的方法。如果在编译时类型未知(在 cmets 中提出),可以使用最通用的searchFile() 方法来检查并相应地重新分配对象。

编辑:作为对多次使用instanceof 的性能影响的说明,consensus appears to be that it's irrelevant, and that the use of modern instanceof is pretty fast anyway

【讨论】:

  • 如果类型在编译时已知,这将起作用。
  • 我同意,但这里不是这样
  • @OliverCharlesworth - 是的。我想他们仍然必须依靠instanceof 来运行运行时。这可能是通用SimpleFile 方法的目的,以适当地重新分配对象。
  • 请注意,当叶类型从另一个叶类型扩展时,instanceof 可能很危险; (例如 XmlFile 扩展了 TextFile)。然后,它取决于如何处理 XmlFile 的语句顺序。您至少必须了解继承层次结构,才能将您的语句按合理的顺序排列。
  • 这完全取决于 searchFile 方法中的逻辑量,但我怀疑您最终会希望将每个方法移到其自己的类中并将通用代码提取到基类中。每个 searchFile 类都有相同的接口。
【解决方案2】:

如果您可以修改File 及其子类,您可以尝试使用访问者模式。既然你不能,我建议你将你的类型测试 switch 语句合并到一个方法中,这样你只需要修改一个 switch。

public Enum FileHandler {
   HTML(){
      public search(File file, String searchstr){ /* ... */}
      public prettyPrint(File file){ /* ... */}
   },
   XML(){ /* ... */};
   // ... end list of enum implementations

   public static FileHander get(File file){
      // Put your master switch statement here
      if (targetPage instanceof HtmlFile) {
         return HTML;
      }
      else if (targetPage instanceof XmlFile) {
         return XML;
      }
      // ...
   }
}

您使用 enum 类的代码看起来像这样,避免了可怕的 switch 语句:

public searchFile(SimpleFile targetFile, String searchStr) {
   FileHandler.get(targetFile).search(targetFile, searchStr);
}

【讨论】:

    【解决方案3】:

    我的方法是使用执行业务逻辑的单一方法创建一个接口FileSearcher。为每种文件类型创建此接口的一个实现。然后,创建一个注册表,将SimpleFile 的子类映射到它们对应的FileSearcher 实现。在您的搜索文件方法中,与其进行instanceof 检查,不如在运行时查找匹配的FileSeacher 实现并委托给它。

    如果有HtmlFile 等的子类,使用此实现可能会遇到麻烦,因为您必须将HtmlFileSearcher 映射到HtmlFile 的所有子类。在这种情况下,向FileSearcher 添加另一个方法,名为canHandle(Class<SimpleFile> runtimeClass),它允许FileSearcher 实现表明他们理解的文件类。在注册表中,而不是在地图中查找它,而是遍历所有已注册的FileSearchers

    【讨论】:

      【解决方案4】:

      您可以使用解释器设计模式或责任链设计模式来解决这个问题。这些模式似乎正是解决这个问题的完美方法。

      解释器设计模式的示例,如果您需要责任链设计模式的示例,请发表评论

      客户:

      public Integer wordCountOnPage (Page samplePage) 
      {
          ArrayList list = new ArrayList<Page>();
          list.add(new XmlPage());
      
          list.add(new HtmlPage());
      
          list.add(new JsonPage());
      
          for (int index = 0 ; index < list.size(); index ++)
      
          {
              Page eachPage = (Page) list.get(index); 
              if (eachPage.intercept(samplePage)){
                  Integer i = eachPage.wordCountOnPage(samplePage);
              }
      
          }
          return 1;
      }
      
      
      public class XmlPage implements Page {
      
      @Override
      public Boolean intercept(Page page) {
          // TODO Auto-generated method stub
          return page instanceof XmlPage;
      }
      
      @Override
      public Integer wordCountOnPage(Page page) {
          // TODO Auto-generated method stub
          return null;
      }
      

      }

      public interface Page {
      
      Boolean intercept(Page page);
      Integer wordCountOnPage(Page page);
      }
      

      【讨论】:

      • 很遗憾,我无法编辑基类或派生类。谢谢!!
      猜你喜欢
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      相关资源
      最近更新 更多