Рефакторинг последовательности сопоставления с образцом - PullRequest
2 голосов
/ 15 апреля 2011

У меня есть следующий фрагмент кода (сокращенно для примера):

while (reader.ready()) {
    String line = reader.readLine();

    Matcher responseCodeMatcher = responseCodePattern.matcher(line); 
    if (responseCodeMatcher.matches()) {
        responseCode = Integer.parseInt(responseCodeMatcher.group(1));
        continue;
    }

    Matcher cacheControlMatcher = cacheControlPattern.matcher(line); 
    if (cacheControlMatcher.matches()) {
        cacheControl = CacheControl.parseString(responseCodeMatcher.group(1));
        continue;
    }

        ...

}

Все шаблоны являются статическими конечными членами класса. Поэтому у меня есть куча шаблонов, и я хочу выяснить для каждой строки, соответствует ли она одному из них, и если да, то сделать что-то (что меняется от шаблона к шаблону). Можете ли вы придумать способ, как это как-то хорошо изменить? Возможно, это «Коллекция шаблонов», которую я изучаю (и как я узнаю, что делать, если она соответствует?) Или какая-то другая идея.

Ответы [ 3 ]

2 голосов
/ 30 апреля 2011

Я закончил рефакторинг следующим образом.Я создал класс HttpPatterns:

package cs236369.proxy.types;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public enum HttpPatterns {
    RESPONSE_CODE("^HTTP/1\\.1 (\\d+) .*$"),
    CACHE_CONTROL("^Cache-Control: (\\w+)$"),
    HOST("^Host: (\\w+)$"),
    REQUEST_HEADER("(GET|POST) ([^\\s]+) ([^\\s]+)$"),
    ACCEPT_ENCODING("^Accept-Encoding: .*$"),
    CONTENT_ENCODING("^Content-Encoding: ([^\\s]+)$");

    private final Pattern pattern;

    HttpPatterns(String regex) {
        pattern = Pattern.compile(regex);
    }

    public boolean matches(String expression) {
        return pattern.matcher(expression).matches();
    }

    public Object process(String expression) {
        Matcher matcher = pattern.matcher(expression);
        if (!matcher.matches()) {
            throw new RuntimeException("Called `process`, but the expression doesn't match. Call `matches` first.");
        }

        if (this == RESPONSE_CODE) {
            return Integer.parseInt(matcher.group(1));
        } else if (this == CACHE_CONTROL) {
            return CacheControl.parseString(matcher.group(1));
        } else if (this == HOST) {
            return matcher.group(1);
        } else if (this == REQUEST_HEADER) {
            return new RequestHeader(RequestType.parseString(matcher.group(1)), matcher.group(2), matcher.group(3));
        } else if (this == CONTENT_ENCODING) {
            return ContentEncoding.parseString(matcher.group(1));
        } else { //never happens
            return null;
        }
    }


}

И я использую его так:

String line;
            while ((line = reader.readLine()) != null) {

                if (HttpPatterns.CACHE_CONTROL.matches(line)) {
                    cacheControl = (CacheControl) HttpPatterns.RESPONSE_CODE.process(line);
                } else if (HttpPatterns.REQUEST_HEADER.matches(line)) {
                    requestHeader = (RequestHeader) HttpPatterns.REQUEST_HEADER.process(line);
                } else if (HttpPatterns.HOST.matches(line)) {
                    requestHost = (String) HttpPatterns.HOST.process(line);
                } else if (HttpPatterns.ACCEPT_ENCODING.matches(line)) {
                    continue;
                } else if (line.isEmpty()) {
                    break;
                }
                fullRequest += "\r\n" + line;
            }

Мне не нравится, что я должен разыгрывать все, что получаю, но этолучшее решение, которое я нашел.

2 голосов
/ 15 апреля 2011

Поскольку до сих пор никто не ответил, я отвечу, хотя я не знаю Java.
В C # я создал бы список кортежей.Элемент 1 кортежа - это шаблон для проверки, а элемент 2 - анонимный метод, который содержит код, специфичный для шаблона, для выполнения.В C # это выглядело бы примерно так:

var patterns = new List<Tuple<Pattern, Action<Matcher>>>();
patterns.Add(Tuple.Create(responseCodePattern, matcher =>
    {
        responseCode = Integer.parseInt(matcher.group(1));
    }));

patterns.Add(Tuple.Create(cacheControlPattern, matcher =>
    {
        cacheControl = CacheControl.parseString(matcher.group(1));
    }));

while (reader.ready()) {
    String line = reader.readLine();
    foreach(var tuple in patterns)
    {
        Matcher matcher = tuple.Item1.matcher(line);
        if(matcher.matches())
        {
            tuple.Item2(matcher);
            break;
        }
    }
}

Я не знаю, имеет ли это какой-то смысл для парня из Java, особенно с синтаксисом лямбды ... Пожалуйста, спросите, если нет: -)

0 голосов
/ 15 апреля 2011

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

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

На стороне действия, шаблон для использования, конечно же, шаблон Command.Есть много возможностей при использовании команды.Если вам не нужен контекст, тогда команда довольно проста, у нее просто есть метод execute.Если вам нужно передать какой-то контекст, который будет меняться, вы можете либо шаблонизировать команды, либо создавать новые на лету, а затем вызывать их.

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