1

微流数据库及其类 StorageConfiguration 有两个问题:

1) New() 和 Builder() 方法与 DEFAULT 构造有什么区别?

2)为什么这些方法都是大写的?这似乎不是 Java 命名约定。

感谢您的任何回答!

4

1 回答 1

1

我是 MicroStream 的首席开发人员,我很乐意回答这些问题。

1)

“新建”是类型本身的“静态工厂方法”。
“Builder”是该类型的“builder”实例的静态工厂方法。

这两个术语都可以完美地在谷歌上搜索以获取有关它们的更多信息。
以快速服务为出发点:

“静态工厂方法”: https ://www.baeldung.com/java-constructors-vs-static-factory-methods

“建造者模式”: https ://en.wikipedia.org/wiki/Builder_pattern

--

对于您实际上的第二个问题,关于“DEFAULT”构造:

如果可以的话,没有“默认”构造,而是“默认”。
(约定很重要……主要是。见下文。)

“默认”只是接口 StorageConfiguration 的默认实现(= 类)。
直接在类中构建软件架构很快就会变得过于死板,从而导致糟糕的设计。引用和实例化类直接为单个实现创建了许多硬编码的依赖项,这些依赖项以后无法更改或变得更加灵活。继承实际上很少有足够的灵活性来解决出现的架构灵活性问题。另一方面,接口只定义一个类型,实现它的实际类几乎不重要,甚至可以轻松互换。例如,通过仅通过接口进行设计,每个实例都可以通过使用装饰器模式轻松地被任何所需的逻辑“包装”。例如,将日志方面添加到类型。

有一篇很好的文章,其中有一篇关于 James Gosling(Java 的发明者)的轶事,名为“Why extends is evil”,描述了这一点:
https://www.javaworld.com/article/2073649/why-extends-is-evil。 html

所以:
“默认”只是实现它所嵌套的接口的默认类。将这样的类命名为“默认”是有意义的,不是吗?它旁边可以有其他类,例如“Wrapper”或“LazyInitializing”或“Dummy”或“Randomizing”等。

这种设计模式被用于 MicroStream 的整个代码中,赋予它极其灵活和强大的架构。例如:
使用一行代码,MicroStream 的每个部分(机器中的每个“齿轮”)都可以由自定义实现替换。一个做不同的事情(也许更好?)或修复错误,甚至不需要新的 MicroStream 版本。或者添加日志记录或自定义异常处理,或者在通常没有对象通信的情况下引入对象通信。可能直接使用应用程序逻辑(但风险自负!)。任何事情都是可能的,至少在接口的边界之内。

一开始对接口的思考可能会令人困惑(这就是为什么许多开发人员用一个适得其反的“I”前缀“烧掉标记”接口。每次看到它都会伤害我),但它们是 Java 中的实际设计类型。类只是它们的实现工具,在设计层面上几乎是无关紧要的。

--

2)

我认为“静态工厂方法”更合适的术语是“伪构造函数”。它是一种充当该类型的公共 API 构造函数的方法,但它不是实际的构造函数。在讨论了这种封装构造函数的静态方法的设计优势之后,我想到了关于最佳、一致的命名模式的问题。JDK 给出了一些不应该复制的糟糕的例子。像“的”或“得到”。这些名称几乎不包含该方法目的的含义。
它应该尽可能简短,但仍应尽可能具有描述性。“创建”或“构建”可以,但它们真的是最好的选择吗?“new” 是最好的,但具有讽刺意味的是,这是一个与构造函数相关的关键字,应该对公共 API 隐藏。“new” 或 “nEw” 看起来非常难看,而且输入起来很麻烦。但是“新”呢?是的,这不是严格的 Java 命名约定。但是已经有一种方法是通用命名规则的例外。哪一个?构造函数!这不是“新人(...”)而是“新人(...)”。以大写字母开头的方法。从Java开始。所以如果静态方法应该代替构造函数,不会 应用同样的例外......或者......命名约定的“扩展”,这是否是非常合乎逻辑和非常好的信号?所以......“新”它是。很短,很清楚。也不再和原来的构造函数非常相似。“Person.New”而不是“新人”。
适合这两种命名异常的“命名约定扩展”是:“每个以大写字母开头的静态方法都保证返回该类型的新实例。” 不是缓存的。总是一个新的。(这有时对于保证算法的正确性至关重要。)

这也有一些巧妙的副作用。例如:

  • 创建“StorageConfigurationBuilder”新实例的伪构造方法可以是“StorageConfiguration.Builder()”。它不言自明,简单明了。
  • 或者如果有一个方法“public static Vector Normalized(Vector v)”,它隐含地告诉传递的实例不会被改变,但是会为标准化的向量值返回一个新的实例。这就像突然可以选择给构造函数正确的名称。而不是一大堆不同的“Vector(...)”方法并且不得不依赖 JavaDoc 来间接解释它们的含义,解释就在名称中。“新建(...)”,“标准化(...)”,“复制(...)”等。

  • 并且它也与嵌套默认类模式很好地配合使用:无需编写“new StorageConfiguration.Default()”(这会很糟糕,因为太硬编码,无论如何),但只需“StorageConfiguration.New”就足够了。它将在内部创建并返回一个新的“StorageConfiguration.Default”实例。如果内部逻辑发生变化,API 用户甚至都不会注意到它。

如果没有其他人这样做,我为什么要这样做?如果有人想一想,那不是一个有效的论点。只要它们有意义,我就非常遵守标准和约定。他们大约有 99% 的时间会这样做,但是如果它们包含问题(例如禁止将静态方法称为“新”)或缺少完全合理的功能(例如 PersonBuilder b = Person.Builder()”或选择正确的名称对于构造函数),然后,经过仔细考虑,我会根据需要扩展它们。这称为创新。如果到目前为止没有其他人有这种洞察力,那对他们不利,而不是对我。问题不在于为什么发明家创造了一种改进,但为什么到目前为止没有人这样做。如果有明显的改进可能性,不能仅仅因为没有人这样做而成为不这样做的正当理由。这样的想法会导致进步的停滞和死亡。就像将自己锁定在 1970 年代的数据存储技术中 40 多年,而不是仅仅采用明显更容易、更快、直接、更好的方式。

我建议将大写字母方法命名扩展视为创新的证明:如果一个新想法客观上带来的优势远远多于劣势,那么它应该 - 或几乎必须 - 完成。

特此请大家采纳。

于 2020-01-19T16:55:43.013 回答