Как я должен возвращать различные типы в методе, основанном на значении строки в Java? - PullRequest
6 голосов
/ 26 апреля 2010

Я новичок в Java, и у меня возникла следующая проблема:

Я создал несколько классов, которые все реализуют интерфейс "Parser". У меня есть JavaParser, PythonParser, CParser и, наконец, TextParser.

Я пытаюсь написать метод, чтобы он брал либо File, либо String (представляющий имя файла) и возвращал соответствующий анализатор с учетом расширения файла.

Вот некоторый псевдо-код того, что я в основном пытаюсь сделать:

public Parser getParser(String filename)  
{
    String extension = filename.substring(filename.lastIndexOf("."));

    switch(extension)
    {
        case "py": return new PythonParser();
        case "java": return new JavaParser();
        case "c": return new CParser();
        default: return new TextParser();
    }
}

В целом, это правильный способ справиться с этой ситуацией? Кроме того, как мне следует учитывать тот факт, что Java не позволяет включать строки? Должен ли я использовать .hashcode () значение строк?

Я чувствую, что есть какой-то шаблон дизайна или что-то для этого, но это ускользает от меня. Это как ты это сделаешь?

Ответы [ 7 ]

11 голосов
/ 26 апреля 2010
  1. Вы можете просто использовать несколько if операторов один за другим.

  2. Вы можете создать специальный enum для своих языков и использовать его в своем выражении switch.

  3. Вы можете использовать Map, где языки являются ключами, а прототипы синтаксического анализатора являются значениями.

3-й вариант выглядит интересно как для меня. Код будет выглядеть так:

return parsers.get(extention).newInstance();

А вот немного хитрая реализация 2-го варианта с enum:

enum Language {

    JAVA {
        public Parser getParser () {
            return new JavaParser ();
        }}, 
    PYTHON {
        public Parser getParser () {
            return new PythonParser ();
        }}, 
    TEXT {
        public Parser getParser () {
            return new TextParser ();
        }};

    public Parser getParser () {
        return null;
    }

    public static Language getLanguage (String extention) {
        try {
            return valueOf (extention);
        } catch (IllegalArgumentException e) {
            return TEXT;
        }
    }
}

...

public Parser getParser(String filename) {
    String extension = filename.substring(filename.lastIndexOf("."));    
    return Language.getLanguage (extension).getParser ();
}
8 голосов
/ 26 апреля 2010

Это называется фабричным шаблоном метода , и это кажется довольно хорошим способом решения вашей проблемы. Одним из способов решения проблемы «Java не разрешает переключение строк» ​​является использование оператора if ... else.

if (extension.equals("py")) {
    return new PythonParser();
}
else if(extension.equals("java")) {
    return new JavaParser();
}
else ...
1 голос
/ 26 апреля 2010

PythonParser, JavaParser и CParser должны быть подклассами Parser, или Parser может быть интерфейсом, который все они реализуют.

Вы можете легко обойти ограничения операторов case, используя if / else if.

НЕ используйте хеш-код строки. Многие строки соответствуют одному и тому же хеш-коду, потому что extension.hashCode () == "java" .hashCode () не обязательно означает, что extension.equals ("java"). При наличии всего трех строк вероятность случайного столкновения мала, но программы, которые «должны работать большую часть времени, пока нам везет», являются плохой новостью. В этом случае программа могла бы совершенно корректно работать с этим конкретным набором строк, а затем через некоторое время вы добавили CobolParser, и он неожиданно перестал работать.

Кто-то упоминал использование перечислений. В целом, гораздо лучше использовать перечисления для определения «типов» вещей, чем использовать строки. Но в этом случае вы извлекаете строку из имени файла, поэтому вам придется выполнить ее через блок if / else if, чтобы преобразовать ее в перечисление, а затем проверить перечисление. Поскольку вы быстро создаете синтаксический анализатор для конкретного типа, маловероятно, что вы сохраните enum или когда-нибудь снова его просматриваете, поэтому я не думаю, что вы получите что-либо.

1 голос
/ 26 апреля 2010

В общем, это правильный путь справиться с этой ситуацией?

Это кажется правильным путем, учитывая, что PythonParser, JavaParser и т. Д. Реализуют или являются подклассами Parser

Кроме того, как я должен справиться с фактом что Java не позволяет включать строки?

Используйте if..else if; Вам не нужно использовать хэш-коды или что-то еще. Проверьте метод equals() для строк.

Я чувствую, что есть какой-то дизайн шаблон или что-то

да

1 голос
/ 26 апреля 2010

вы можете использовать enum, чтобы один раз решить, с каким языком вы имеете дело, а затем использовать его (также в выражениях switch):

enum Type { JAVA, PYTHON, CPP, C, PERL };

Type getType(String filename)
{
  // do your if chain here
  return JAVA;
}

public Parser getParser(String filename)  
{
    switch(getType(filename))
    {
        case PYTHON: return new PythonParser();
        case JAVA: return new JavaParser();
        case C: return new CParser();
        default: return new TextParser();
    }
}

Относительно вашего первого вопроса: ваш подход в порядке, он называется фабричным шаблоном , когда у вас есть что-то, что создает много видов конкретных реализаций интерфейса (в вашем случае Parser), выбирая какой во время выполнения

0 голосов
/ 26 апреля 2010

Я бы:

public Parser getParser(String filename)  {
    String extension = filename.substring(filename.lastIndexOf("."));

    if( "py".equals(extension)  ) { return new PythonParser() }
    if( "java".equals(extension)) { return new JavaParser();  }
    if( "c".equals(extension)   ) { return new CParser();     }

    return new TextParser();
}

О вопросе в вашем заголовке

Как я должен возвращать разные типы в методе на основе значения строки в Java?

Вы не можете возвращать только один тип. Что вы можете сделать, так это вернуть подтипы этого типа, поэтому, если ваши парсеры являются подклассами или реализуют класс / интерфейс Parser, приведенный выше код будет работать отлично.

т.

public interface Parser {
    //parser methods  
    ...
}
class PythonParser implements Parser {
     // parser implementation
     ...
}
class JavaParser implements Parser {
     // parser implementation
     ...
}
class CParser implements Parser {
     // parser implementation
     ...
}
0 голосов
/ 26 апреля 2010

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

Что касается регистра переключателя, если вы используете jdk1.5 или выше, вы должны определить enum для этих строк, и вы легко можете использовать регистр swicth.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...