2015-07-16 5 views
3

У меня есть два вспомогательных методов:Вызвать метод, основанный на типе объекта

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.... 
} 

Однако, это не кажется, эл egant и было интересно, если есть лучший способ ..

ответ

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.... 
} 
+0

Это нарушает принцип * Open-Closed *. Ваш класс должен быть изменен для каждого нового источника ввода. – CKing

+0

Нет, вы можете просто расширить класс с помощью новой 'overload (NewInputSourceType)' overload – CupawnTae

+0

Но вы не получите того преимущества, что сможете решить во время выполнения, с каким источником загрузить данные с помощью этого подхода. Перегруженные методы разрешаются во время компиляции. – CKing

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) 
} 
+0

Это нарушает очень прочный (каламбур) принцип проектирования, известный как принцип открытого закрывания. Не имеет значения, используете ли вы 'equals' или' instanceof', вы должны изменить свой класс каждый раз, когда хотите добавить новую технику для загрузки данных. – CKing

3

Вы правы: литье необходимо, но неэлегантно.

Другой способ сделать это, если вам нравится шаблон дизайна GoF - это шаблон посетителя, а также двойная отправка.

Третий способ - использовать отражение Java.

+0

Мне было бы очень приятно, если бы вы могли исправить свой ответ, чтобы показать пример с отражением. –

+0

Извините, нет времени. Почему бы вам не разобраться и представить его? – duffymo

1

Перегрузите также метод работы. Используйте данные, которые вы получили, для вызова метода, который принимает строку.

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 
} 
+0

Мне это нравится. Это действительно не сокращает количество строк coce, но по крайней мере ограничивает, какие типы объектов могут быть переданы для работы() –

5

Однако, это не кажется элегантным и было интересно, если есть лучший путь.

Это верно. Это нарушает принцип Open-Closed. Класс должен быть открыт для расширения, но закрыт для модификации. Вы также правы, когда говорите, что вам нужен общий объект.Вот что вы можете сделать:

Создание интерфейса загрузчика

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. Обратите внимание, как вы контролируете, какой метод используется для загрузки данных, создавая экземпляр соответствующего класса. Еще одно преимущество заключается в том, что вы можете решить, какой метод использовать в runtime, создав Factory, который вводит пользователя и создает соответствующий конкретный подкласс. Это упрощенная версия Strategy pattern.

Отказ от ответственности: Образцы кода, представленные выше, не были проверены на ошибки компиляции, так как у меня нет Java.

+0

Можете ли вы также объяснить, как это можно использовать в методе 'operation()'? – Codebender

+0

@CupawnTae Я подробно остановился на том, что имел в виду, когда сказал: «В реальном мире клиентский код будет зависеть от интерфейса« Loader »* в более ранней версии моего ответа. Надеюсь, что моя разработка прояснила тот момент, который я делал, что соответствует тому, что было задано ОП. – CKing

+0

@Codebender Я разработал свой ответ, чтобы развернуть заявление, которое я сделал ранее »* В реальном мире клиентский код будет зависеть от интерфейса Loader *« – CKing

Смежные вопросы