Избегание нескольких операторов 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.

Ответ 1

Вы можете использовать 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;
    }
}

Для этого кода вам нужно будет определить 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";

Ответ 2

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

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

Ответ 3

Лично у меня нет проблем с операторами 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.

Ответ 4

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

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

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

Ответ 5

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

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

  • Легко читаемый и понятный кто-либо (по моему опыту, программисты среднего уровня часто недооценивают это и обычно предпочитают ходить с мозаичными шаблонами, которые в конце концов не читаются вообще для подавляющего большинства программистов, которые не знают этого конкретного шаблона)
  • Вся информация находится в одном месте. Как указывал 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"; 
   }

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

Ответ 6

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

Ответ 7

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

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));
}

Обратите внимание, что использование операторов 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 буквы, вам придется немного изменить его, если он может быть длиннее или короче).

Ответ 8

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

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

String mimeType = URLConnection.guessContentTypeFromName(filename);

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

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

Ответ 9

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

Ответ 10

Как насчет сопоставления расширений с типами 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;
 } 

Ответ 11

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

Ответ 12

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

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

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

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

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

Ответ 13

Командный шаблон - путь. Вот один пример использования 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);
  }
}

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