使用管道流實現Java 8階段構建器
Step builder多階段步驟構造器模式是一種物件建立軟體設計模式。與傳統構建器模式進行比較時,步驟構建器模式提供了一些簡潔的好處。Step Builder模式的主要優勢之一是為客戶提供有關如何使用API的指南。它可以看作是構建器模式和狀態機的混合,事實上,這種模式通常被稱為構建物件的嚮導。
優點
- 透過物件建立過程逐步為API提供使用者指南。
- 一旦物件處於一致狀態,API使用者就可以呼叫構建器的build()方法。
- 減少了建立不一致物件例項的機會。
- 對必填欄位進行排序初始化。
- 流暢的API。
- 無需為欄位驗證提供validate()方法。
缺點
- 實現模式本身所需的程式碼可讀性低。
- 沒有eclipse外掛來幫助程式碼生成。(另一方面,Builder模式生成器有很多程式碼生成器)。
案例:
由於Step Builder模式是一種建立性設計模式,因此我們將重點放在其目的 - 建立物件上。
API使用示例如下所示:
Email email = Email.builder().from(EmailAddress.of("Microservices Weekly <mw@microservicesweekly.com>")) .to(EmailAddress.of("svlada@gmail.com")) .subject(Subject.of("Subject")) .content(Content.of("Test email")) .build(); |
這個API內部是如何實現的?
public class Email { private EmailAddress from; private List<EmailAddress> to; private List<EmailAddress> cc; private List<EmailAddress> bcc; private Subject subject; private Content content; public static FromStep builder() { return new Builder(); } public interface FromStep { ToStep from(EmailAddress from); } public interface ToStep { SubjectStep to(EmailAddress... from); } public interface SubjectStep { ContentStep subject(Subject subject); } public interface ContentStep { Build content(Content content); } public interface Build { Email build(); Build cc(EmailAddress... cc); Build bcc(EmailAddress... bcc); } public static class Builder implements FromStep, ToStep, SubjectStep, ContentStep, Build { private EmailAddress from; private List<EmailAddress> to; private List<EmailAddress> cc; private List<EmailAddress> bcc; private Subject subject; private Content content; @Override public Email build() { return new Email(this); } @Override public Build cc(EmailAddress... cc) { Objects.requireNonNull(cc); this.cc = new ArrayList<EmailAddress>(Arrays.asList(cc)); return this; } @Override public Build bcc(EmailAddress... bcc) { Objects.requireNonNull(bcc); this.bcc = new ArrayList<EmailAddress>(Arrays.asList(bcc)); return this; } @Override public Build content(Content content) { Objects.requireNonNull(content); this.content = content; return this; } @Override public ContentStep subject(Subject subject) { Objects.requireNonNull(subject); this.subject = subject; return this; } @Override public SubjectStep to(EmailAddress... to) { Objects.requireNonNull(to); this.to = new ArrayList<EmailAddress>(Arrays.asList(to)); return this; } @Override public ToStep from(EmailAddress from) { Objects.requireNonNull(from); this.from = from; return this; } } private Email(Builder builder) { this.from = builder.from; this.to = builder.to; this.cc = builder.cc; this.bcc = builder.bcc; this.subject = builder.subject; this.content = builder.content; } public EmailAddress getFrom() { return from; } public List<EmailAddress> getTo() { return to; } public List<EmailAddress> getCc() { return cc; } public List<EmailAddress> getBcc() { return bcc; } public Subject getSubject() { return subject; } public Content getContent() { return content; } } |
實施的經驗法則:
- 向您的類新增依賴項。建議將private修飾符新增到類屬性中。
- 將每個建立步驟定義為基類中的內部介面。
- 每個建立步驟都應該返回鏈中的下一步(介面)。
- 最後一步應該是名為“Build”的介面,它將提供build()方法。
- 定義一個內部靜態Builder類,它實現所有已定義的步驟。
- 實現步驟介面方法。
新案例:
public static class Coffee { private final CoffeeType type; // Compulsory, one of arabica, robusta, moka... private final Quantity quantity; // Compulsory private final Optional<Quantity> sugar; private final Optional<Quantity> cream; } @FunctionalInterface interface RequireCoffeeType { RequireQuantity coffeeType(CoffeeType type); } @FunctionalInterface interface RequireQuantity { FinalStage quantity(Quantity quantity); } public final class FinalStage { private final CoffeeType type; // Obtained through the staged builder private final Quantity quantity; // Obtained through the staged builder private Optional<Quantity> sugar; // Regular builder for this optional field private Optional<Quantity> cream; // Regular builder for this optional field // .... public Coffee build() { return new Coffee(type, quantity, sugar, cream); } } public static RequireCoffeeType builder() { return type -> quantity -> new FinalStage(type, quantity); } |
它可以強制呼叫者呼叫所有階段,最終獲得構建方法,並確保不會忘記強制階段,
遵循這種模式很難:
- 很難重用上面定義的階段(功能介面)
- 很難在階段提出替代選擇。
讓我們提出我們想要構建的以下事件:
class UserConnected implements Event { private final User user; private final MailboxSession.SessionId sessionId; // Constructor & getters } class MailboxCreated implements Event { private final User user; private final MailboxSession.SessionId sessionId; private final MailboxId mailboxId; // Constructor & getters } |
這是兩個建立事件,分別是使用者連線上的事件和郵箱已經建立的事件。由於我們的事件具有類似的結構,我們最終會得到大量重複的程式碼!
使用當前模式定義,分階段構建器看起來像這樣,沒有其他選擇,並且階段重用:
public static class UserConnectedBuilder { @FunctionalInterface public interface RequireUser { RequireSessionId user(User user); } @FunctionalInterface public interface RequireSessionId { FinalStage sessionId(MailboxSession.SessionId sessionId); } public static class FinalStage { private final User user; private final MailboxSession.SessionId sessionId; // constructor public UserConnected build() { return new UserConnected(user, sessionId); } } public static RequireUser builder() { return user -> sessionId -> new FinalStage(user, sessionId); } } public static class MailboxCreatedBuilder { @FunctionalInterface public interface RequireUser { RequireSessionId user(User user); } @FunctionalInterface public interface RequireSessionId { RequireMailboxId sessionId(MailboxSession.SessionId sessionId); } @FunctionalInterface public interface RequireMailboxId { FinalStage mailboxId(MailboxId mailboxId); } public static class FinalStage { private final User user; private final MailboxSession.SessionId sessionId; private final MailboxId mailboxId; // constructor public MailboxCreated build() { return new MailboxCreated(user, sessionId, mailboxId); } } public static RequireUser builder() { return user -> sessionId -> mailboxId -> new FinalStage(user, sessionId, mailboxId); } } |
由於我們的事件具有類似的結構,我們最終會得到大量重複的程式碼!
我們可以看到,作為呼叫者,我們還需要明確指定每個階段:
MailboxCreatedBuilder.builder() .user(User.fromUsername("bob")) .sessionId(SessionId.of(45)) .mailboxId(MailboxId.of(15)) .build(); MailboxCreatedBuilder.builder() // .mailboxSession(session) // not allowed .user(session.getUser()) .sessionId(session.getId()) .mailboxId(MailboxId.of(15)) .build(); |
希望我們可以使用一些Java特異功能來克服這些限制......
具有泛型的獨立階段
透過使我們的階段成為通用的,我們可以讓呼叫者指定下一個階段(透過構建器方法簽名),這將使階段重用和解除彼此之間的階段。
使用預設方法的替代(跳過階段)
我們可以定義將兩個階段組合在一起的“元階段”。然後,“元階段”可以公開一種預設方法,允許將兩個階段分解為單個階段。
上面的例子現在看起來像這樣:
@FunctionalInterface public interface RequireUser<T> { T user(User user); } @FunctionalInterface public interface RequireSessionId<T> { T sessionId(MailboxSession.SessionId sessionId); } @FunctionalInterface // "meta-stage" session combining to stages into one public interface RequireSession<T> extends RequireUser<RequireSessionId<T>> { default T session(MailboxSession session) { return user(session.getUser()) .sessionId(session.getId()); } } @FunctionalInterface public interface RequireMailboxId<T> { T mailboxId(MailboxId mailboxId); } public static class UserConnectedBuilder { public static class FinalStage { private final User user; private final MailboxSession.SessionId sessionId; // constructor public UserConnected build() { return new UserConnected(user, sessionId); } } public static RequireSession<FinalStage> builder() { return user -> sessionId -> new FinalStage(user, sessionId); } } public static class MailboxCreatedBuilder { public static class FinalStage { private final User user; private final MailboxSession.SessionId sessionId; private final MailboxId mailboxId; // constructor public MailboxCreated build() { return new MailboxCreated(user, sessionId, mailboxId); } } public static RequireSession<RequireMailboxId<FinalStage>> builder() { return user -> sessionId -> mailboxId -> new FinalStage(user, sessionId, mailboxId); } } |
現在,使用者可以獲得所需的便捷方法,更不用說程式碼共享了......
MailboxCreatedBuilder.builder() .user(User.fromUsername("bob")) .sessionId(SessionId.of(45)) .mailboxId(MailboxId.of(15)) .build(); MailboxCreatedBuilder.builder() .mailboxSession(session) // now allowed .mailboxId(MailboxId.of(15)) .build(); |
此外,構建器方法型別顯式地向呼叫者公開所需的階段,而不是僅暴露下一個階段......
相關文章
- 實戰 | 使用 Kotlin Flow 構建資料流 "管道"Kotlin
- Java 8中實現構建器模式Java模式
- Dockerfile 多階段構建實踐Docker
- Docker多階段構建最佳實踐Docker
- 使用 Docker 開發 - 使用多階段構建映象Docker
- 現階段Kubernetes架構的8個問題架構
- Docker 分階段構建映象Docker
- Docker 映象分階段構建Docker
- Docker多階段構建實戰(multi-stage builds)DockerUI
- 統一過程(UP)定義了初啟階段、精化階段、構建階段、移交階段和產生階段,每個階段以達到某個里程碑時結束,其中()的里程碑是生命週期架構。 A.初啟階段 B.精化階段 C.構建階段 D.移交階段架構
- Java開發工程師進階篇-Java8的Stream流使用技巧Java工程師
- 最新拓薪Java高階階段及ERP實戰專案(階段三)Java
- Io流階段大總結
- 前端使用 Konva 實現視覺化設計器(18)- 素材巢狀 - 載入階段前端視覺化巢狀
- JAVA實現節流閥Java
- list轉map,使用java8,stream流Java
- Docker 重要更新: 原生支援多階段構建(multi-stage build)DockerUI
- 最課程階段大作業之01:使用SVN實現版本控制
- 初學Java的5個階段,你在哪個階段?Java
- 最課程階段大作業02:實現自己的利息計算器
- [譯] 使用 Recompose 來構建高階元件元件
- 阿里Java學習路線:階段 1:Java語言基礎-Java語言高階特性:第16章:位元組流與字元流:課時77:位元組流與字元流的區別阿里Java字元
- 使用Maven構建Java專案MavenJava
- 淺談java8中的流的使用Java
- js--事件流、事件委託、事件階段JS事件
- Java8——Stream流Java
- java 8 特性——stream流Java
- 從資料流角度管窺 Moya 的實現(一):構建請求
- Java學習第一階段Java
- Java進階面試系列階段性總結【石杉的架構筆記】Java面試架構筆記
- 匿名管道通訊實現
- java進階(33)--IO流Java
- Java中實現流的分割槽Java
- Java的通過管道來實現執行緒通訊Java執行緒
- 實體店實現有效會員管理的五個階段
- Java 8 Stream並行流Java並行
- laravel 使用schema構建器,新增構建表的列型別Laravel型別
- 使用 LangChain 構建聊天機器人LangChain機器人