内容简介:Step builder多阶段步骤构造器模式是一种对象创建软件设计模式。与传统构建器模式进行比较时,步骤构建器模式提供了一些简洁的好处。Step Builder模式的主要优势之一是为客户提供有关如何使用API的指南。它可以看作是构建器模式和状态机的混合,事实上,这种模式通常被称为构建对象的向导。优点缺点
Step builder多阶段步骤构造器模式是一种对象创建软件设计模式。与传统构建器模式进行比较时,步骤构建器模式提供了一些简洁的好处。Step Builder模式的主要优势之一是为客户提供有关如何使用API的指南。它可以看作是构建器模式和状态机的混合,事实上,这种模式通常被称为构建对象的向导。
优点
- 通过对象创建过程逐步为API提供用户指南。
- 一旦对象处于一致状态,API用户就可以调用构建器的build()方法。
- 减少了创建不一致对象实例的机会。
- 对必填字段进行 排序 初始化。
- 流畅的API。
- 无需为字段验证提供validate()方法。
缺点
- 实现模式本身所需的代码可读性低。
- 没有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; } }
实施的经验法则:
- 向您的类添加依赖项。建议将private修饰符添加到类属性中。
- 将每个创建步骤定义为基类中的内部接口。
- 每个创建步骤都应该返回链中的下一步(界面)。
- 最后一步应该是名为“Build”的接口,它将提供build()方法。
- 定义一个内部静态Builder类,它实现所有已定义的步骤。
- 实现步骤接口方法。
新案例:
<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>
此外,构建器方法类型显式地向调用者公开所需的阶段,而不是仅暴露下一个阶段......
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 为异构大数据环境构建数据管道
- 为异构的大数据运行环境构建数据管道
- Apache Hudi + CDC 管道构建实时数据湖
- 如何轻松构建适用于 K8S 的 CI/CD 管道?
- 速度不够,管道来凑——Redis管道技术
- Golang pipline泛型管道和类型管道的性能差距
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。