【问题标题】:Example of Builder Pattern in Java API?Java API 中的构建器模式示例?
【发布时间】:2010-01-30 19:46:05
【问题描述】:

Joshua Bloch 的Effective Java 描述了一种构建器模式,该模式可用于构建具有多个可选自定义参数的对象。他为 Builder 函数建议的命名约定,“模拟在 Ada 和 Python 中找到的命名可选参数”,似乎不符合 Java 的标准命名约定。 Java 函数往往依赖于一个动词来启动函数,然后是一个基于名词的短语来描述它的作用。 Builder 类只有该函数要定义的变量的名称。

Java 标准库中是否有任何使用构建器模式的 API?在继续使用本书之前,我想将书中的建议与核心 Java 库中的实际实现进行比较。

【问题讨论】:

标签: java design-patterns builder


【解决方案1】:

我不确定核心 JDK 是否包含在内,但可以在 Guava 中找到很好的示例。 MapMaker 可能是我能想到的最好的例子。例如,来自文档:

ConcurrentMap<Key, Graph> graphs = new MapMaker()
    .concurrencyLevel(32)
    .softKeys()
    .weakValues()
    .expiration(30, TimeUnit.MINUTES)
    .makeComputingMap(
        new Function<Key, Graph>() {
          public Graph apply(Key key) {
            return createExpensiveGraph(key);
          }
        });

是的,这类事情可能违背“标准”Java 命名的原则,但它也可以非常易读。

对于不返回“this”而是返回新对象(通常具有不可变类型)的情况,我喜欢“with”前缀 - Joda Time 广泛使用该模式。这不是建造者模式,而是另一种相关的建造形式。

【讨论】:

  • 这种“with”前缀的用法是更常用的约定吗?对于返回 this 的构建器类,我总是使用 with 前缀。我认为“with”在语义上也暗示了这一点:具有属性的实例=修改它。
  • @kap:是的,“with”前缀是一个相当普遍的约定,我个人认为“with”修改其他内容没有意义。请注意,这很常见,以至于在 C# 9 中它已成为 语言 的一部分(就为记录类型生成的内容而言):github.com/dotnet/csharplang/blob/master/proposals/records.md
  • @kap: java.time 也非常广泛地使用它,例如LocalDate.withMonth
【解决方案2】:

Locale 类有一个 Builder 模式的示例。 https://docs.oracle.com/javase/7/docs/api/java/util/Locale.Builder.html

用法:

Locale locale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();

【讨论】:

    【解决方案3】:

    唯一对 Effective java 书籍最准确的构建器是 StringBuilder。 我从示例中看到的唯一区别是此构建器不是 String 的内部类。

    所有方法都将构建器对象返回到链中。而 toString() 方法就是 build() 方法。

    【讨论】:

    • StringBuilder 不是 Builder Pattern 的实现,只是因为它的名字说 builder,它不会返回一个新的Object,而是相反。和平。
    • @AnilBhaskar 您能否详细说明为什么StringBuilder 不遵循构建器模式?在这个实现中,至少创建了一个新的(防御性的)Stringgithub.com/openjdk-mirror/jdk7u-jdk/blob/…
    【解决方案4】:

    Java 8 Core API 的一个很好的例子是Calendar,例如你可以使用:

    Calendar cal = new Calendar.Builder().setCalendarType("iso8601")
                            .setWeekDate(2013, 1, MONDAY).build();
    

    Java 7 的另一个很好的例子是 Locale,使用:

    Locale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
    

    构建器模式在不可变对象的上下文中最有用。有趣的是,Java 中有许多可变构建器,StringBuilder 是最常见的一种。 Java 8 中的可变构建器:

    • Stream.Builder
    • IntStream.Builder
    • LongStream.Builder
    • DoubleStream.Builder

    【讨论】:

      【解决方案5】:

      SAXParser 似乎是一个很好的例子:

      • SAXParser - 导演
      • ContentHandler - 建设者

      SAXParser的典型用法与Builder相同:

      // Create Director
      SAXParser parser = new org.apache.xerces.parsers.SAXParser();  
      // Create Concrete Builder (our own class)
      IndentingContentHandler handler = new IndentingContentHandler();
      // Set Builder to Director
      parser.setContentHandler(handler);
      // Build
      parser.parse(new InputSource(new FileReader(fileName));
      // Get indented XML as String from handler
      String indentedXML = handler.getResult();
      

      【讨论】:

        【解决方案6】:

        ProcessBuilder 在很大程度上是构建器模式的一个实例,但并不完全使用 java 命名约定。

        ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2"); 映射 env = pb.environment(); env.put("VAR1", "myValue"); env.remove("OTHERVAR"); env.put("VAR2", env.get("VAR1") + "suffix"); pb.directory(new File("myDir")); 进程 p = pb.start();

        在 SQL 包中,PreparedStatement 可以被认为是构建器模式的一个实例:

        PreparedStatement stmt = conn.prepareStatement(getSql()); stmt.setString(1, ...); stmt.setString(2, ...); 结果集 rs = stmt.executeQuery(); ... stmt.setString(2, ...); rs = stmt.executeQuery(); ...

        【讨论】:

        • 如果你不能链接方法,这不是一个完全值得的构建器模式。
        【解决方案7】:

        它只是在标准库中定义(未实现),然而,JDBC DataSource 对象让我想起了构建器模式。您创建一个 DataSource 对象,然后设置一些属性,然后建立连接。

        这是一个代码示例...

        DataSource ds = (DataSource)ctx.lookup("jdbc/AcmeDB");
        ds.setServerName("my_database_server");
        ds.setDescription("the data source for inventory and personnel");
        Connection con = ds.getConnection("genius", "abracadabra");
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-02
          • 2015-08-24
          • 2020-02-13
          • 1970-01-01
          相关资源
          最近更新 更多