使用管道流实现Java 8阶段构建器

栏目: Java · 发布时间: 6年前

内容简介:Step builder多阶段步骤构造器模式是一种对象创建软件设计模式。与传统构建器模式进行比较时,步骤构建器模式提供了一些简洁的好处。Step Builder模式的主要优势之一是为客户提供有关如何使用API​​的指南。它可以看作是构建器模式和状态机的混合,事实上,这种模式通常被称为构建对象的向导。优点缺点

Step builder多阶段步骤构造器模式是一种对象创建软件设计模式。与传统构建器模式进行比较时,步骤构建器模式提供了一些简洁的好处。Step Builder模式的主要优势之一是为客户提供有关如何使用API​​的指南。它可以看作是构建器模式和状态机的混合,事实上,这种模式通常被称为构建对象的向导。

优点

  1. 通过对象创建过程逐步为API提供用户指南。
  2. 一旦对象处于一致状态,API用户就可以调用构建器的build()方法。
  3. 减少了创建不一致对象实例的机会。
  4. 对必填字段进行 排序 初始化。
  5. 流畅的API。
  6. 无需为字段验证提供validate()方法。

缺点

  1. 实现模式本身所需的代码可读性低。
  2. 没有eclipse插件来帮助代码生成。(另一方面,Builder模式生成器有很多代码生成器)。

案例:

由于Step Builder模式是一种创建性设计模式,因此我们将重点放在其目的 - 创建对象上。

API使用示例如下所示:

Email email =  
Email.builder().from(EmailAddress.of(<font>"Microservices Weekly <mw@microservicesweekly.com>"</font><font>))  
    .to(EmailAddress.of(</font><font>"svlada@gmail.com"</font><font>))
    .subject(Subject.of(</font><font>"Subject"</font><font>))
    .content(Content.of(</font><font>"Test email"</font><font>))
    .build();
</font>

这个API内部是如何实现的?

<b>public</b> <b>class</b> Email {  
    <b>private</b> EmailAddress from;
    <b>private</b> List<EmailAddress> to;
    <b>private</b> List<EmailAddress> cc;
    <b>private</b> List<EmailAddress> bcc;
    <b>private</b> Subject subject;
    <b>private</b> Content content;

    <b>public</b> <b>static</b> FromStep builder() {
        <b>return</b> <b>new</b> Builder();
    }

    <b>public</b> <b>interface</b> FromStep {
        ToStep from(EmailAddress from);
    }

    <b>public</b> <b>interface</b> ToStep {
        SubjectStep to(EmailAddress... from);
    }

    <b>public</b> <b>interface</b> SubjectStep {
        ContentStep subject(Subject subject);
    }

    <b>public</b> <b>interface</b> ContentStep {
        Build content(Content content);
    }

    <b>public</b> <b>interface</b> Build {
        Email build();
        Build cc(EmailAddress... cc);
        Build bcc(EmailAddress... bcc);
    }

    <b>public</b> <b>static</b> <b>class</b> Builder implements FromStep, ToStep, SubjectStep, ContentStep, Build {
        <b>private</b> EmailAddress from;
        <b>private</b> List<EmailAddress> to;
        <b>private</b> List<EmailAddress> cc;
        <b>private</b> List<EmailAddress> bcc;
        <b>private</b> Subject subject;
        <b>private</b> Content content;

        @Override
        <b>public</b> Email build() {
            <b>return</b> <b>new</b> Email(<b>this</b>);
        }
        @Override
        <b>public</b> Build cc(EmailAddress... cc) {
            Objects.requireNonNull(cc);
            <b>this</b>.cc = <b>new</b> ArrayList<EmailAddress>(Arrays.asList(cc));
            <b>return</b> <b>this</b>;
        }
        @Override
        <b>public</b> Build bcc(EmailAddress... bcc) {
            Objects.requireNonNull(bcc);
            <b>this</b>.bcc = <b>new</b> ArrayList<EmailAddress>(Arrays.asList(bcc));
            <b>return</b> <b>this</b>;
        }
        @Override
        <b>public</b> Build content(Content content) {
            Objects.requireNonNull(content);
            <b>this</b>.content = content;
            <b>return</b> <b>this</b>;
        }
        @Override
        <b>public</b> ContentStep subject(Subject subject) {
            Objects.requireNonNull(subject);
            <b>this</b>.subject = subject;
            <b>return</b> <b>this</b>;
        }
        @Override
        <b>public</b> SubjectStep to(EmailAddress... to) {
            Objects.requireNonNull(to);
            <b>this</b>.to = <b>new</b> ArrayList<EmailAddress>(Arrays.asList(to));
            <b>return</b> <b>this</b>;
        }
        @Override
        <b>public</b> ToStep from(EmailAddress from) {
            Objects.requireNonNull(from);
            <b>this</b>.from = from;
            <b>return</b> <b>this</b>;
        }
    }

    <b>private</b> Email(Builder builder) {
        <b>this</b>.from = builder.from;
        <b>this</b>.to = builder.to;
        <b>this</b>.cc = builder.cc;
        <b>this</b>.bcc = builder.bcc;
        <b>this</b>.subject = builder.subject;
        <b>this</b>.content = builder.content;
    }

    <b>public</b> EmailAddress getFrom() {
        <b>return</b> from;
    }

    <b>public</b> List<EmailAddress> getTo() {
        <b>return</b> to;
    }

    <b>public</b> List<EmailAddress> getCc() {
        <b>return</b> cc;
    }

    <b>public</b> List<EmailAddress> getBcc() {
        <b>return</b> bcc;
    }

    <b>public</b> Subject getSubject() {
        <b>return</b> subject;
    }

    <b>public</b> Content getContent() {
        <b>return</b> content;
    }
}

实施的经验法则:

  1. 向您的类添加依赖项。建议将private修饰符添加到类属性中。
  2. 将每个创建步骤定义为基类中的内部接口。
  3. 每个创建步骤都应该返回链中的下一步(界面)。
  4. 最后一步应该是名为“Build”的接口,它将提供build()方法。
  5. 定义一个内部静态Builder类,它实现所有已定义的步骤。
  6. 实现步骤接口方法。

新案例:

<b>public</b> <b>static</b> <b>class</b> Coffee {
        <b>private</b> <b>final</b> CoffeeType type; <font><i>// Compulsory, one of arabica, robusta, moka...</i></font><font>
        <b>private</b> <b>final</b> Quantity quantity; </font><font><i>// Compulsory</i></font><font>
        <b>private</b> <b>final</b> Optional<Quantity> sugar;
        <b>private</b> <b>final</b> Optional<Quantity> cream;
    }

    @FunctionalInterface
    <b>interface</b> RequireCoffeeType {
        RequireQuantity coffeeType(CoffeeType type);
    }

    @FunctionalInterface
    <b>interface</b> RequireQuantity {
        FinalStage quantity(Quantity quantity);
    }

    <b>public</b> <b>final</b> <b>class</b> FinalStage {
        <b>private</b> <b>final</b> CoffeeType type; </font><font><i>// Obtained through the staged builder</i></font><font>
        <b>private</b> <b>final</b> Quantity quantity; </font><font><i>// Obtained through the staged builder</i></font><font>
        <b>private</b> Optional<Quantity> sugar; </font><font><i>// Regular builder for this optional field</i></font><font>
        <b>private</b> Optional<Quantity> cream; </font><font><i>// Regular builder for this optional field</i></font><font>

        </font><font><i>// ....</i></font><font>

        <b>public</b> Coffee build() {
            <b>return</b> <b>new</b> Coffee(type, quantity, sugar, cream);
        }
    }

    <b>public</b> <b>static</b> RequireCoffeeType builder() {
        <b>return</b> type -> quantity -> <b>new</b> FinalStage(type, quantity);
    }
</font>

它可以强制调用者调用所有阶段,最终获得构建方法,并确保不会忘记强制阶段,

遵循这种模式很难:

  • 很难重用上面定义的阶段(功能接口)
  • 很难在阶段提出替代选择。

让我们提出我们想要构建的以下事件:

 <b>class</b> UserConnected implements Event {
        <b>private</b> <b>final</b> User user;
        <b>private</b> <b>final</b> MailboxSession.SessionId sessionId;
        
        <font><i>// Constructor & getters</i></font><font>
    }
    
    <b>class</b> MailboxCreated implements Event {
        <b>private</b> <b>final</b> User user;
        <b>private</b> <b>final</b> MailboxSession.SessionId sessionId;
        <b>private</b> <b>final</b> MailboxId mailboxId;

        </font><font><i>// Constructor & getters</i></font><font>
    }
</font>

这是两个创建事件,分别是用户连接上的事件和邮箱已经创建的事件。由于我们的事件具有类似的结构,我们最终会得到大量重复的代码!

使用当前模式定义,分阶段构建器看起来像这样,没有其他选择,并且阶段重用:

<b>public</b> <b>static</b> <b>class</b> UserConnectedBuilder {
        @FunctionalInterface
        <b>public</b> <b>interface</b> RequireUser {
            RequireSessionId user(User user);
        }

        @FunctionalInterface
        <b>public</b> <b>interface</b> RequireSessionId {
            FinalStage sessionId(MailboxSession.SessionId sessionId);
        }

        <b>public</b> <b>static</b> <b>class</b> FinalStage {
            <b>private</b> <b>final</b> User user;
            <b>private</b> <b>final</b> MailboxSession.SessionId sessionId;
            <font><i>// constructor</i></font><font>

            <b>public</b> UserConnected build() {
                <b>return</b> <b>new</b> UserConnected(user, sessionId);
            }
        }

        <b>public</b> <b>static</b> RequireUser builder() {
            <b>return</b> user -> sessionId -> <b>new</b> FinalStage(user, sessionId);
        }
    }

    <b>public</b> <b>static</b> <b>class</b> MailboxCreatedBuilder {
        @FunctionalInterface
        <b>public</b> <b>interface</b> RequireUser {
            RequireSessionId user(User user);
        }

        @FunctionalInterface
        <b>public</b> <b>interface</b> RequireSessionId {
            RequireMailboxId sessionId(MailboxSession.SessionId sessionId);
        }

        @FunctionalInterface
        <b>public</b> <b>interface</b> RequireMailboxId {
            FinalStage mailboxId(MailboxId mailboxId);
        }

        <b>public</b> <b>static</b> <b>class</b> FinalStage {
            <b>private</b> <b>final</b> User user;
            <b>private</b> <b>final</b> MailboxSession.SessionId sessionId;
            <b>private</b> <b>final</b> MailboxId mailboxId;
            </font><font><i>// constructor</i></font><font>

            <b>public</b> MailboxCreated build() {
                <b>return</b> <b>new</b> MailboxCreated(user, sessionId, mailboxId);
            }
        }

        <b>public</b> <b>static</b> RequireUser builder() {
            <b>return</b> user -> sessionId -> mailboxId -> <b>new</b> FinalStage(user, sessionId, mailboxId);
        }
    }
</font>

由于我们的事件具有类似的结构,我们最终会得到大量重复的代码!

我们可以看到,作为调用者,我们还需要明确指定每个阶段:

MailboxCreatedBuilder.builder()
            .user(User.fromUsername(<font>"bob"</font><font>))
            .sessionId(SessionId.of(45))
            .mailboxId(MailboxId.of(15))
            .build();

        MailboxCreatedBuilder.builder()
            </font><font><i>// .mailboxSession(session) // not allowed</i></font><font>
            .user(session.getUser())
            .sessionId(session.getId())
            .mailboxId(MailboxId.of(15))
            .build();
</font>

希望我们可以使用一些 Java 特异功能来克服这些限制......

具有泛型的独立阶段

通过使我们的阶段成为通用的,我们可以让调用者指定下一个阶段(通过构建器方法签名),这将使阶段重用和解除彼此之间的阶段。

使用默认方法的替代(跳过阶段)

我们可以定义将两个阶段组合在一起的“元阶段”。然后,“元阶段”可以公开一种默认方法,允许将两个阶段分解为单个阶段。

上面的例子现在看起来像这样:

 @FunctionalInterface
    <b>public</b> <b>interface</b> RequireUser<T> {
        T user(User user);
    }

    @FunctionalInterface
    <b>public</b> <b>interface</b> RequireSessionId<T> {
        T sessionId(MailboxSession.SessionId sessionId);
    }

    @FunctionalInterface <font><i>// "meta-stage" session combining to stages into one</i></font><font>
    <b>public</b> <b>interface</b> RequireSession<T> <b>extends</b> RequireUser<RequireSessionId<T>> {
        <b>default</b> T session(MailboxSession session) {
            <b>return</b> user(session.getUser())
                .sessionId(session.getId());
        }
    }

    @FunctionalInterface
    <b>public</b> <b>interface</b> RequireMailboxId<T> {
        T mailboxId(MailboxId mailboxId);
    }

    <b>public</b> <b>static</b> <b>class</b> UserConnectedBuilder {
        <b>public</b> <b>static</b> <b>class</b> FinalStage {
            <b>private</b> <b>final</b> User user;
            <b>private</b> <b>final</b> MailboxSession.SessionId sessionId;
            </font><font><i>// constructor</i></font><font>

            <b>public</b> UserConnected build() {
                <b>return</b> <b>new</b> UserConnected(user, sessionId);
            }
        }

        <b>public</b> <b>static</b> RequireSession<FinalStage> builder() {
            <b>return</b> user -> sessionId -> <b>new</b> FinalStage(user, sessionId);
        }
    }

    <b>public</b> <b>static</b> <b>class</b> MailboxCreatedBuilder {
        <b>public</b> <b>static</b> <b>class</b> FinalStage {
            <b>private</b> <b>final</b> User user;
            <b>private</b> <b>final</b> MailboxSession.SessionId sessionId;
            <b>private</b> <b>final</b> MailboxId mailboxId;
            </font><font><i>// constructor</i></font><font>

            <b>public</b> MailboxCreated build() {
                <b>return</b> <b>new</b> MailboxCreated(user, sessionId, mailboxId);
            }
        }

        <b>public</b> <b>static</b> RequireSession<RequireMailboxId<FinalStage>> builder() {
            <b>return</b> user -> sessionId -> mailboxId -> <b>new</b> FinalStage(user, sessionId, mailboxId);
        }
    }
</font>

现在,用户可以获得所需的便捷方法,更不用说代码共享了......

 MailboxCreatedBuilder.builder()
            .user(User.fromUsername(<font>"bob"</font><font>))
            .sessionId(SessionId.of(45))
            .mailboxId(MailboxId.of(15))
            .build();

        MailboxCreatedBuilder.builder()
            .mailboxSession(session) </font><font><i>// now allowed</i></font><font>
            .mailboxId(MailboxId.of(15))
            .build();
</font>

此外,构建器方法类型显式地向调用者公开所需的阶段,而不是仅暴露下一个阶段......


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mathematica Cookbook

Mathematica Cookbook

Sal Mangano / O'Reilly Media / 2009 / GBP 51.99

As the leading software application for symbolic mathematics, Mathematica is standard in many environments that rely on math, such as science, engineering, financial analysis, software development, an......一起来看看 《Mathematica Cookbook》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试