Подтвердить что ты не робот

Класс Java Pattern не имеет открытого конструктора, почему?

Я просматривал библиотеку Java Regex, удивлен тем, что класс Pattern не имеет публичного конструктора, который я принимал как должное в течение многих лет.

Одна из причин, по которой я подозреваю, что статический метод compile используется в пользу конструктора, может заключаться в том, что конструктор всегда будет возвращать новый объект, тогда как статический метод может вернуть ранее созданный (и кешированный) объект при условии, что строка шаблона то же самое.

Однако это не так, как показано ниже.

public class PatternCompiler {
    public static void main(String[] args) {
        Pattern first = Pattern.compile(".");
        Pattern second = Pattern.compile(".");
        if (first == second) {
            System.out.println("The same object has been reused!");
        } else {
            System.out.println("Why not just use constructor?");
        }
    }
}

Любые другие сильные аргументы в пользу использования статического метода над конструктором?

Изменить: здесь был найден вопрос. Ни один из ответов там не убедил меня. Прочитав все ответы, я чувствую, что статический метод имеет немало преимуществ перед публичным конструктором в отношении создания объекта, но не наоборот. Это правда? Если это так, я собираюсь создать такие статические методы для каждого из моих классов и смело предположить, что он более читабельным и гибким.

4b9b3361

Ответ 1

Как правило, класс не будет иметь публичный конструктор по одной из трех причин:

  • Класс - это класс утилиты, и нет никаких оснований для его создания (например, java.lang.Math).
  • Мгновенное действие может завершиться неудачно, и конструктор не сможет вернуть null.
  • Статический метод разъясняет смысл того, что происходит во время создания экземпляра.

В классе Pattern применим третий случай - статический метод compile используется исключительно для ясности. Построение шаблона через new Pattern(..) не имеет смысла с объяснительной точки зрения, потому что существует сложный процесс, который продолжает создавать новый Pattern. Чтобы объяснить этот процесс, статический метод называется compile, потому что регулярное выражение по существу скомпилировано для создания шаблона.

Короче говоря, нет никакой программной цели для создания Pattern только конструктивного с помощью статического метода.

Ответ 2

Одна из возможных причин заключается в том, что таким образом кеширование может быть добавлено позже в метод.

Другая возможная причина - читаемость. Рассмотрим этот (часто цитируемый) объект:

class Point2d{
  static Point2d fromCartesian(double x, double y);
  static Point2d fromPolar(double abs, double arg);
}

Point2d.fromCartesian(1, 2) и Point2d.fromPolar(1, 2) оба прекрасно читаемы и недвусмысленны (ну... кроме порядка аргумента).

Теперь рассмотрим new Point2d(1, 2). Являются ли аргументы декартовыми координатами или полярными координатами? Это еще хуже, если конструкторы со сходными/совместимыми сигнатурами имеют совершенно другую семантику (скажем, int, int является декартовой, double, double является полярной).

Это обоснование применимо к любому объекту, который может быть построен несколькими различными способами, которые не отличаются только типом аргумента. В то время как Pattern в настоящее время может быть только compile d из регулярного выражения, в будущем могут появиться различные представления шаблона (возможно, тогда compile - это имя плохого метода).

Другая возможная причина, упомянутая @Vulcan, заключается в том, что конструктор не должен терпеть неудачу.

Если Pattern.compile встречается с недопустимым шаблоном, он выдает PatternSyntaxException. Некоторые люди считают неправильной практикой бросать исключение из конструктора. При этом FileInputStream делает именно это. Аналогично, если конструктивное решение заключалось в возврате null из метода compile, это было бы невозможно с конструктором.


Короче говоря, конструктор не является хорошим выбором дизайна, если:

  • может выполняться кэширование или
  • конструктор семантически неоднозначен или
  • создание может завершиться неудачей.

Ответ 3

Это просто дизайнерское решение. В этом случае нет "реального" преимущества. Однако этот проект позволяет оптимизировать (например, кэширование) без изменения API. См. http://gbracha.blogspot.nl/2007/06/constructors-considered-harmful.html

Ответ 4

Factory имеют несколько преимуществ, некоторые из которых уже указаны в других ответах. Рекомендации по рассмотрению методов factory вместо конструкторов - это даже первая глава в книге "Эффективная Java" от Джошуа Блоха (обязательная для каждого программиста Java).


Одно из преимуществ заключается в том, что у вас может быть несколько методов factory, которые имеют одинаковые сигнатуры параметров, но разные имена. Этого нельзя достичь с помощью конструкторов.

Например, может потребоваться создать Pattern из нескольких форматов ввода, все из которых просто String s:

class Pattern {
  compile(String regexp) { ... }
  compileFromJson(String json) { ... }
  compileFromXML(String xml) { ... }
}

Даже если вы не делаете этого при создании класса, методы factory дают вам возможность добавлять такие методы последними, не вызывая странности.

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

class Ugly {
  Ugly(String str) { ... }

  /* This constructor interpretes str in some other way.
   * The second parameter is ignored completely. */
  Ugly(String str, boolean ignored) { ... }
}

К сожалению, я не могу вспомнить имя такого класса, но я думаю, что он даже был в Java API.


Еще одно преимущество, о котором не упоминалось ранее, заключается в том, что при использовании методов factory в сочетании с конструкторами private-private вы можете запретить суб-классификацию для других, но все же сами используете подклассы. В случае Pattern вы можете иметь частные подклассы, такие как CompiledPattern, LazilyCompiledPattern и InterpretedPattern, но все же запретить подкласс для обеспечения неизменности.

С помощью публичного конструктора вы можете либо запретить субклассирование для всех, либо вообще не использовать.

Ответ 5

Он имеет частный конструктор.

 /**
     * This private constructor is used to create all Patterns. The pattern
     * string and match flags are all that is needed to completely describe
     * a Pattern. An empty pattern string results in an object tree with
     * only a Start node and a LastNode node.
     */
    private Pattern(String p, int f) {

и compile вызывает вызов.

public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }

Поскольку вы используете == сравнение, которое для ссылок, оно не будет работать

Единственная причина, по которой я могу думать об этом поведении, это то, что флаг соответствия будет по умолчанию равен нулю в методе compile, который действует методом factory.

Ответ 6

Если вы действительно хотите совершить глубокое погружение, погрузитесь в архивы JSR 51.

Регулярные выражения были введены как часть JSR 51, в которых вы все еще можете найти проектные решения в своих архивах, http://jcp.org/en/jsr/detail?id=51