【问题标题】:Call a method based on the object type根据对象类型调用方法
【发布时间】:2015-07-16 15:48:10
【问题描述】:

我有两个辅助方法:

public String load(URL url) {...}
public String load(File file) {...}

我想要一个方法,根据它接收到的对象类型调用适当的辅助方法:

public void operate(Object object) {...}

我知道有一种复杂的方法是:

public void operate(Object object) {
    String data = null;
    if (object.getClass().equals(File.class)) {
        data = load((File) object);
    }
    if (object.getClass().equals(URL.class)) {
        data = load((URL) object);
    }
    // operate on the data....
}

但是,这似乎并不优雅,并且很好奇是否有更好的方法..

【问题讨论】:

    标签: java generics design-patterns


    【解决方案1】:

    但是,这似乎并不优雅,并且很好奇是否有更好的方法。

    没错。这违反了开闭原则。一个类必须对扩展开放但对修改关闭。当您说您需要一个通用对象时,您也是正确的。这是你可以做的:

    创建加载器界面

    public interface Loader<T> {
       public String load(T t);
    }
    

    创建一个从文件加载的加载器

    public class FileLoader implements Loader<File> {
         public String load(File f) { 
                //load from file 
         }    
    }
    

    创建一个从 Url 加载的加载器

    public class UrlLoader implements Loader<Url> {
         public String load(URL r) { 
                //load from url 
         }    
    }
    

    创建一个对数据进行操作的类

     class DataOperator<T> {
        Loader<T> loader;
        public SomeClient(Loader<T> loader) {
           this.loader = loader;
        }
    
        public void operate(T inputSource) {
           String data = loader.load(inputSource);
           //operate on the data
        }
    
     }
    

    然后客户端代码可以使用上面的API,如下所示:

    DataOperator<File> fileDataOperator = new DataOperator<>(new FileLoader());
    fileDataOperator.operate(new File("somefile.txt"));
    
    DataOperator<URL> urlDataOperator = new DataOperator<>(new UrlLoader());
    urlDataOperator.operate(new URL("http://somesite.com")); 
    

    您可能会认为这是用于解决一个简单问题的许多类。然而,这实际上与众所周知的 Open-Closed 设计原则是一致的。请注意如何通过创建适当类的实例来控制用于加载数据的技术。您获得的另一个优势是,您可以通过创建Factory 来决定在runtime 使用哪种技术,该Factory 接受用户输入并创建适当的具体子类。这是Strategy pattern 的简化版本。

    免责声明:上面提供的代码示例没有经过编译错误测试,因为我在这台机器上没有Java

    【讨论】:

    • 您能否解释一下如何在operate() 方法中使用它?
    • @CupawnTae 我已经详细说明了我在我的早期版本中所说的“在现实世界中,客户端代码将依赖于Loader 接口”的意思。回答。我希望我的阐述已经澄清了我提出的观点,这与 OP 的要求一致。
    • @Codebender 我已经详细阐述了我的答案,以扩展我之前所做的声明“在现实世界中,客户端代码将取决于加载器接口
    • @CKing 奇怪,我从未见过该评论,也没有在编辑历史记录中看到它 - 它是否在后续编辑中被汇总?无论如何,我认为您的编辑使其更清晰
    • @CupawnTae 我刚刚查看了编辑历史,发现我之前的声明丢失了。这很奇怪,因为我把那句话复制到了我的评论中。
    【解决方案2】:

    一个稍微不那么复杂的方式是instanceof,例如

    if (object instanceof File)) {
        data = load((File) object);
    }
    

    但是,大多数情况下,使用 instanceof 表示您想要实现的目标有更好的结构,例如

    public void operate(File file) {
        operate(load(file));
    }
    
    public void operate(URL url) {
        operate(load(url));
    }
    
    public void operate(String data) {
        // operate on the data....
    }
    

    【讨论】:

    • 这违反了开闭原则。必须针对每个新的输入源修改您的类。
    • 不,你可以用一个新的operate(NewInputSourceType)重载来扩展类
    • 但是您不会获得能够在运行时决定使用这种方法从哪个源加载数据的优势。重载的方法在编译时解析。
    • 正确,此解决方案仅在编译时已知类型的情况下才有效
    • 是的。 OCP 需要多态替换。扩展类并添加新的operate(NewInputSourceType) 重载不会让您多态地替换实现。如果从File 加载数据的客户端代码必须开始从NewInputSourceType 加载数据,则必须将其更改为使用具有此方法的类的引用。如果每个新技术都需要更改客户端代码,那么遵循 OCP 的类有什么好处? OCP 也应该扩展到客户端代码。请参阅我的答案中的DataOperator,任何输入都不会改变。
    【解决方案3】:

    您可以使用 instanceof 并检查它,然后强制转换对象并调用方法:

    if (obj instanceof File) {
       ((File) obj).method();
    }
    else if (obj instanceof URL) {
       ((URL) obj).method();
    }
    

    反之亦然:

    if (obj instanceof File) {
     load((File) obj)
    }
    else if (obj instanceof URL) {
       load((URL) obj)
    }
    

    【讨论】:

    • 这违反了一个非常可靠(双关语)的设计原则,即开闭原则。不管你使用equals 还是instanceof,每次你想添加新的数据加载技术时都必须修改你的类。
    【解决方案4】:

    你是对的:强制转换是必要的,但不优雅。

    如果您喜欢 GoF 设计模式,另一种方法是访问者模式,即双重调度。

    第三种方法是使用 Java 反射。

    【讨论】:

    • 如果你能修改你的答案以展示一个带有反思的例子,我会很高兴的。
    • 抱歉,没时间。你为什么不把它弄出来并呈现出来呢?
    【解决方案5】:

    我只是偶然发现了同样的问题,并发现了一种使用反射的不同方法:

    public void operate(Object object) {
        Method method = this.getClass().getDeclaredMethod("load", object.getClass());
        String data = (String) method.invoke(this, object);
        // [...]
    }
    

    当然,它伴随着处理相当多异常的负担。

    【讨论】:

      【解决方案6】:

      也重载操作方法。使用你收到的数据来调用一个接受字符串的方法。

      public static void operate(URL url) {
          String data = load(url);
          doOperations(data);
      }
      
      public static void operate(File file) {
          String data = load(file);
          doOperations(data);
      }
      
      private static void doOperations(String data) {
          //TODO Do something with data
      }
      

      【讨论】:

      • 我喜欢这个。它并没有真正减少 coce 的行数,但至少它限制了可以将哪些对象类型传递给 operation()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-31
      • 1970-01-01
      • 2011-05-12
      • 2018-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多