Избегание нескольких операторов If в Java

Я закодировал метод что-то вроде этого. Но я думаю, что это должно пройти рефакторинг. Может ли кто-нибудь предложить лучший подход, чтобы избежать использования этих нескольких операторов if?

private String getMimeType(String fileName){
 if(fileName == null) {
 return ""; 
 } 
 if(fileName.endsWith(".pdf")) {
 return "application/pdf"; 
 }
 if(fileName.endsWith(".doc")) {
 return "application/msword"; 
 }
 if(fileName.endsWith(".xls")) {
 return "application/vnd.ms-excel"; 
 }
 if(fileName.endsWith(".xlw")) {
 return "application/vnd.ms-excel"; 
 }
 if(fileName.endsWith(".ppt")) {
 return "application/vnd.ms-powerpoint"; 
 }
 if(fileName.endsWith(".mdb")) {
 return "application/x-msaccess"; 
 }
 if(fileName.endsWith(".rtf")) {
 return "application/rtf"; 
 }
 if(fileName.endsWith(".txt")) {
 return "txt/plain"; 
 }
 if(fileName.endsWith(".htm") || fileName.endsWith(".html")) {
 return "txt/html"; 
 }
 return "txt/plain"; 
}

Я не могу использовать switch-case здесь, так как мое условие есть java.lang.String.

13 ответов

Вы можете использовать Map для хранения ваших решений:

Map<string,string> extensionToMimeType = new HashMap<string,string>();
extensionToMimeType.put("pdf", "application/pdf");
extensionToMimeType.put("doc", "application/msword");
// and the rest
int lastDot = fileName.lastIndexOf(".");
String mimeType;
if (lastDot==-1) {
 mimeType = NO_EXTENSION_MIME_TYPE;
} else {
 String extension = fileName.substring(lastDot+1);
 mimeType = extensionToMimeType.get(extension);
 if (mimeType == null) {
 mimeType = UNKNOWN_EXTENSION_MIME_TYPE;
 }
}
</string,string></string,string>

Для этого кода вам нужно будет определить NO_EXTENSION_MIME_TYPE и UNKNOWN_EXTENSION_MIME_TYPE, как в вашем классе, примерно так:

private static final String NO_EXTENSION_MIME_TYPE = "application/octet-stream";
private static final String UNKNOWN_EXTENSION_MIME_TYPE = "text/plain";


С помощью HashMap возможно?

Таким образом, вы можете сделать myMap.get(mystr);


как использовать библиотеку обнаружения MIME?

  • mime-util
  • mime4j
  • Библиотека JMimeMagic - бесплатно. Использует расширение файла и магические заголовки для определения типа MIME.
  • mime-util - бесплатно. Использует расширение файла и магические заголовки для определения типа MIME.
  • DROID (идентификация объекта цифровой записи) - бесплатно. Использует автоматизацию пакетной обработки для обнаружения типов MIME.
  • Aperture Framework - бесплатно. Структура для обхода внешних источников для идентификации типов MIME.

(не стесняйтесь добавлять больше, там так много библиотек..)


Лично у меня нет проблем с операторами if. Код читаем, потребовалось всего миллисекунды, чтобы понять, что вы делаете. Это частный метод в любом случае, и если список типов mime является статическим, то нет необходимости переместить сопоставление в файл свойств и использовать таблицу поиска (карту). Карта уменьшит количество строк кода, но чтобы понять код, вы вынуждены читать код и реализацию сопоставления - либо статический инициализатор, либо внешний файл.

Вы можете немного изменить код и использовать перечисление:

private enum FileExtension { NONE, DEFAULT, PDF, DOC, XLS /* ... */ }
private String getMimeType(String fileName){
 String mimeType = null;
 FileExtension fileNameExtension = getFileNameExtension(fileName);
 switch(fileNameExtension) {
 case NONE:
 return "";
 case PDF:
 return "application/pdf";
 // ...
 case DEFAULT:
 return "txt/plain"; 
 }
 throw new RuntimeException("Unhandled FileExtension detected");
}

Метод getFileNameExtension(String fileName) будет просто возвращать значение перечисления для файла fileName, FileExtension.NONE, если fileName пуст (или null?) и FileExtension.DEFAULT, если расширение файла не сопоставлено типу mime.


Я считаю ваш подход лучшим в целом. Это происходит после того, как я проверил несколько разных подходов.

В вашем текущем подходе я вижу ряд огромных преимуществ, а именно:

  • Легко читаемый и понятный кто-либо (по моему опыту, программисты среднего уровня часто недооценивают это и обычно предпочитают ходить с мозаичными шаблонами, которые в конце концов не читаются вообще для подавляющего большинства программистов, которые не знают этого конкретного шаблона)
  • Вся информация находится в одном месте. Как указывал Andreas_D, охота вокруг файлов или классов не является хорошим вариантом для тех, кому нужно исправить ошибку, пока вы в отпуске!
  • Легко поддерживаемый: я мог бы "F3" (если вы Eclipse-ing) в методе и добавлять новый тип контента за считанные секунды, не опасаясь вводить ошибки!

Я могу предложить несколько вещей в любом случае:

  • Этот метод является очень общей целью: Почему это должно быть частным?! Это общедоступный метод некоторого класса утилиты/помощника! Кроме того, это должен быть статический метод ! Вам ничего не нужно из самого объекта для выполнения ваша работа!
  • Вы можете использовать отступы, чтобы сделать вещи красивее и компактны. я знаю этот отступ является своего рода религия для большинства из нас, но я думайте, что это не должно быть строгое правило; его следует правильно использовать для наш код более доступен для чтения и компактный. Если это будет файл конфигурации, вы вероятно, будет иметь что-то вроде:
pdf=application/pdf
doc=application/msword

У вас может быть очень похожий результат:

public static String getMimeType(String fileName){
 if(fileName == null) return "";
 if(fileName.endsWith(".pdf")) return "application/pdf";
 if(fileName.endsWith(".doc")) return "application/msword";
 if(fileName.endsWith(".xls")) return "application/vnd.ms-excel"; 
 return "txt/plain"; 
 }

Это то, на что похоже много реализаций на основе карты.


Нет никакого способа избежать этого вообще. В вашем случае - если есть набор разрешенных расширений - вы можете создать Enum, преобразовать расширение в тип Enum с помощью valueOf(), а затем вы можете переключить свое перечисление.


Самый простой и короткий путь для этой конкретной проблемы - использовать встроенные методы Java SE или EE.

Либо в клиентском приложении "plain vanilla" (которое выводит эту информацию с базовой платформы):

String mimeType = URLConnection.guessContentTypeFromName(filename);

Или в веб-приложении JSP/Servlet (которое выводит эту информацию из файлов web.xml):

String mimeType = getServletContext().getMimeType(filename);


Я бы сделал это, поместив ассоциации в карту, а затем используя карту для поиска:

Map<string, string=""> map = new HashMap<string, string="">();
map.put(".pdf", "application/pdf");
map.put(".doc", "application/msword");
// ... etc.
// For lookup:
private String getMimeType(String fileName) {
 if (fileName == null || fileName.length() < 4) {
 return null;
 }
 return map.get(fileName.substring(fileName.length() - 4));
}
</string,></string,>

Обратите внимание, что использование операторов switch для строк - одна из предлагаемых новых функций для следующей версии Java; см. эту страницу для получения более подробной информации и пример того, как это будет выглядеть в Java 7:

switch (fileName.substring(fileName.length() - 4)) {
 case ".pdf": return "application/pdf";
 case ".doc": return "application/msword";
 // ...
 default: return null;

(edit: Мое решение предполагает, что расширение файла всегда 3 буквы, вам придется немного изменить его, если он может быть длиннее или короче).


Как насчет сопоставления расширений с типами MIME, а затем с использованием цикла? Что-то вроде:

Map<string,string> suffixMappings = new HashMap<string,string>();
suffixMappings.put(".pdf", "application/pdf");
...
private String getMimeType(String fileName){
 if (fileName == null) {
 return ""; 
 }
 String suffix = fileName.substring(fileName.lastIndexOf('.'));
 // If fileName might not have extension, check for that above!
 String mimeType = suffixMappings.get(suffix); 
 return mimeType == null ? "text/plain" : mimeType;
 } 
</string,string></string,string>


Создайте перечисление с именем MimeType с 2 строковыми переменными: расширение и тип. Создайте соответствующий конструктор и передайте значения ".xxx" и "application/xxx". Создайте метод для поиска. Вы можете использовать перечисления в коммутаторе.


Вы всегда можете использовать класс Groovy здесь, так как он позволяет использовать коммутатор для строк:)


Просто упомянем: прямой эквивалент вашего кода не будет использовать карту для прямого поиска (поскольку для этого требуется, чтобы каждое расширение имело ровно 3 символа), но цикл for:

...
Map<string, string=""> extmap = GetExtensionMap();
for (Map.Entry<string,string> entry: extmap.entrySet())
 if (fileName.endsWith(entry.getKey))
 return entry.getValue();
...
</string,string></string,>

Это решение работает с расширениями любой длины, но менее реалистично, чем поиск хэшей, конечно (и немного менее эффективный, чем исходное решение).

Решение алгоритмического дизайна-парня

Более эффективным способом было бы реализовать древовидную структуру, начиная с последнего символа расширения и сохраняя соответствующие типы MIME в соответствующих узлах. Затем вы можете спуститься по дереву, начиная с последнего символа имени файла. Но это, вероятно, перебор...


Командный шаблон - путь. Вот один пример использования java 8:

1. Определите интерфейс:

public interface ExtensionHandler {
 boolean isMatched(String fileName);
 String handle(String fileName);
}

2. Внедрить интерфейс с каждым расширением:

public class PdfHandler implements ExtensionHandler {
 @Override
 public boolean isMatched(String fileName) {
 return fileName.endsWith(".pdf");
 }
 @Override
 public String handle(String fileName) {
 return "application/pdf";
 }
}

и

public class TxtHandler implements ExtensionHandler {
 @Override public boolean isMatched(String fileName) {
 return fileName.endsWith(".txt");
 }
 @Override public String handle(String fileName) {
 return "txt/plain";
 }
}

и т.д.

3. Определите клиента:

public class MimeTypeGetter {
 private List<extensionhandler> extensionHandlers;
 private ExtensionHandler plainTextHandler;
 public MimeTypeGetter() {
 extensionHandlers = new ArrayList<>();
 extensionHandlers.add(new PdfHandler());
 extensionHandlers.add(new DocHandler());
 extensionHandlers.add(new XlsHandler());
 // and so on
 plainTextHandler = new PlainTextHandler();
 extensionHandlers.add(plainTextHandler);
 }
 public String getMimeType(String fileExtension) {
 return extensionHandlers.stream()
 .filter(handler -> handler.isMatched(fileExtension))
 .findFirst()
 .orElse(plainTextHandler)
 .handle(fileExtension);
 }
}
</extensionhandler>

4. И это результат выборки:

public static void main(String[] args) {
 MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();
 System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
 System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
 System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-powerpoint"
 }

licensed under cc by-sa 3.0 with attribution.