我一直在检讨的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?");
}
}
}
背后采用静态方法在构造函数的任何其他强理由?
编辑 :我发现了一个相关的问题在这里。 答案都不存在说服我要么。 通过所有答案阅读,我得到的是一个静态方法在关于创建对象而不是周围的其他方式的公共构造具有相当多的优势的感觉。 真的吗? 如果是的话,我会创造我的班级每一个这样的静态方法和有把握地认为,这既是更具可读性和灵活。
一般来说,一类不会有三种原因之一的公共构造函数:
- 类是一个工具类,也没有理由实例化(例如,java.lang.Math中)。
- 实例化可能会失败,并且构造不能返回
null
。 - 静态方法阐明背后实例化过程中会发生什么意思。
在类的Pattern
,第三种情况是适用-静态compile
方法只用于为清楚起见。 通过构建模式new Pattern(..)
不会使从一个角度解释一点意义,因为有一个复杂的过程,接着创建一个新的Pattern
。 为了解释这个过程中,静态方法被命名为compile
,因为正则表达式基本上编译为创建模式。
总之,存在对使得没有编程目的Pattern
通过一个静态方法仅施工的。
一个可能的原因是,这样一来,缓存可以在以后被添加到方法。
另一个可能的原因是可读性。 考虑这个(经常被引用)对象:
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,一个模式的不同表示可能会在未来(admittably,那么, compile
是一个坏的方法名)。
另一个可能的原因,通过@Vulcan提到的,是一个构造函数不应该失败。
如果Pattern.compile
遇到无效的模式,它抛出一个PatternSyntaxException
。 有些人可能会认为这是一个不好的做法,从构造函数抛出异常。 Admittably, 的FileInputStream正是这么做的。 同样,如果设计决定是返回null
从compile
方法,这是不可能有一个构造函数。
总之,一个构造函数是不是一个很好的设计选择,如果:
- 缓存可能会发生,或
- 构造函数是语义含糊不清,或
- 创建可能会失败。
这仅仅是一个设计决策。 在这种情况下,没有“真正”的优势。 然而,这样的设计允许在不改变API优化(缓存实例)。 见http://gbracha.blogspot.nl/2007/06/constructors-considered-harmful.html
工厂方法有几个优点,其中一些在其他的答案已经被指定。 建议考虑工厂方法代替构造函数的是,即使在大书“有效的Java”从约书亚布洛赫(一不可不看的每一个Java程序员)的第一个篇章。
一个优点是,你可以有它们具有相同的参数签名,但不同的名称几个工厂方法。 这个你不能构造实现。
例如,人们可能希望创建一个Pattern
从几个输入格式,所有这些都只是String
S:
class Pattern {
compile(String regexp) { ... }
compileFromJson(String json) { ... }
compileFromXML(String xml) { ... }
}
即使你不这样做,当你创建类,工厂方法给你加后者,而不会造成古怪这种方法的能力。
举例来说,我已经看到了类哪里需要一个新的构造后来和特殊的意义 - 更少的第二个参数必须添加到第二构造,以允许超载。 显然,这是非常难看:
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中。
尚未前面提到的另一个优点是,在联合工厂方法有包私有构造函数,你可以禁止子类为他人,但仍使用子类自己。 在的情况下Pattern
,你可能希望有像私人的子类CompiledPattern
, LazilyCompiledPattern
和InterpretedPattern
,但仍然禁止子类,以确保不变性。
与公共构造函数,你可以禁止子类的每个人,或者根本没有。
如果你真的想采取深潜,投身到JSR 51的档案。
正则表达式已经被引入作为JSR 51的一部分,这就是你仍然会发现在他们的档案的设计决策, http://jcp.org/en/jsr/detail?id=51
它有一个私人的构造函数。
/**
* 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);
}
由于您使用==比较这对引用它不会工作
我能想到这种行为的唯一原因是,比赛标志将在被默认值为0 compile
作用工厂方法的方法。