Рассмотрим следующий пример, где классы TextFile
, XmlFile
, HtmlFile
, ShellScriptFile
и т.д. - все подклассы класса 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()
для вышеуказанного случая?
Я ценю ваши комментарии или предложения!
Сначала я бы сказал, что ваш пример совсем не выглядит плохим. Это немного многословный и спорный, но не такой сплоченный, как разделение их на разные методы, но для меня это выглядит нормально. Я думаю, что более аккуратный подход может заключаться в использовании перегрузки. Я не могу говорить о разнице в скорости, но с точки зрения обслуживания и расширяемости в будущем будет легче справиться.
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
версию метода SimpleFile
. В случае, если тип не известен во время компиляции (как searchFile()
в комментариях), наиболее общий searchFile()
может использоваться для проверки и перераспределения объекта соответствующим образом.
Редактирование: как примечание о влиянии производительности на множественное использование instanceof
, консенсус, по-видимому, заключается в том, что он не имеет значения и что использование современного экземпляра довольно быстро в любом случае.
Если вы можете изменить File
и его подклассы, вы можете попробовать использовать шаблон посетителя. Поскольку вы не можете, я рекомендую вам консолидировать оператор switch type type в одном методе, так что вам нужно будет только изменить один коммутатор.
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);
}
Моим подходом было бы создать интерфейс FileSearcher
с единственным методом, который выполняет бизнес-логику. Создайте одну реализацию этого интерфейса для каждого типа файла. Затем создайте реестр, который сопоставляет подклассы SimpleFile
с их соответствующей реализацией FileSearcher
. В вашем методе файла поиска, а не в выполнении проверок instanceof
, найдите соответствующую реализацию FileSeacher
во время выполнения и делегируйте ее.
С этой реализацией вы можете столкнуться с проблемами, когда есть подклассы HtmlFile
и т.д., Потому что вам нужно будет сопоставить HtmlFileSearcher
со всеми подклассами HtmlFile
. В этом случае добавьте еще один метод в FileSearcher
именем canHandle(Class<SimpleFile> runtimeClass)
который позволяет реализациям FileSearcher
сигнализировать, какой класс файлов они понимают. В реестре, а не просматривая его на карте, пройдите через все зарегистрированные FileSearchers
.
для решения этой проблемы вы можете использовать шаблон интерпретатора-дизайна-шаблона или шаблона-схемы ответственности-дизайна. эти шаблоны кажутся просто идеальным решением для этой проблемы.
пример шаблона-интерпретатора-шаблона, комментарий, если вам нужен пример шаблона цепочки ответственности-дизайна
Клиент:
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);
}