<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          建造者(Builder)模式的 Rust 實(shí)現(xiàn)

          共 4638字,需瀏覽 10分鐘

           ·

          2021-10-28 19:17

          面向?qū)ο缶幊讨?,設(shè)計(jì)模式是很火的。然而,這些年新出的語言,不完全是面向?qū)ο蟮摹?/span>比如 Rust、Go 等。那相關(guān)的設(shè)計(jì)模式可以在這些語言中實(shí)現(xiàn)嗎?本文講解 Builder (建造者)模式的 Rust 實(shí)現(xiàn)。


          我們知道,Rust 函數(shù)不支持可選參數(shù)、命名參數(shù),也不支持函數(shù)重載。為了克服這一限制 Rust 開發(fā)者經(jīng)常應(yīng)用建造者模式。它需要一些額外的編碼,但從 API 人體工程學(xué)的角度來看,它具有與命名參數(shù)和可選參數(shù)類似的效果。

          01 問題簡介

          考慮以下 Rust 結(jié)構(gòu)體:

          struct?User?{
          ????email:?Option<String>,
          ????first_name:?Option<String>,
          ????last_name:?Option<String>
          }

          在 Ruby 中,持有相同數(shù)據(jù)的類可以定義為:

          class?User
          ??attr_reader?:email,?:first_name,?:last_name

          ??def?initialize(email:?nil,?first_name:?nil,?last_name:?nil)
          ????@email?=?email
          ????@first_name?=?first_name
          ????@last_name?=?last_name
          ??end
          end

          不懂 Ruby 沒關(guān)系,我只想讓你看到,通過明確指定相關(guān)字段來顯示用戶創(chuàng)建實(shí)例是多么容易:

          greyblake?=?User.new(
          ??email:?"[email protected]",
          ??first_name:?"Sergey",
          )

          last_name 沒傳遞,因此它會自動獲得默認(rèn)值:nil

          02 初始化 Rust 結(jié)構(gòu)體

          由于我們在 Rust 中沒有默認(rèn)參數(shù),因此為了初始化此類結(jié)構(gòu),我們必須列出所有字段:

          let?greyblake?=?User?{
          ????email:?Some("[email protected]".to_string()),
          ????first_name:?Some("Sergey".to_string()),
          ????last_name:?None,
          }

          這與 Ruby 的命名參數(shù)非常相似,但我們必須設(shè)置所有字段,即使 last_nameNone,你也得顯示設(shè)置??赡苣阌X得沒啥,但對于大型復(fù)雜的結(jié)構(gòu),可能就有點(diǎn)煩人了。

          當(dāng)然,我們可以創(chuàng)建一個(gè)實(shí)現(xiàn)構(gòu)造器:new()

          impl?User?{
          ????fn?new(
          ????????email:?Option<String>,
          ????????first_name:?Option<String>,
          ????????last_name:?Option<String>
          ????)?->?Self?{
          ????????Self?{?email,?first_name,?last_name?}
          ????}
          }

          這時(shí)這么使用:

          let?greyblake?=?User::new(
          ????Some("[email protected]".to_string()),
          ????Some("Sergey".to_string()),
          ????None
          )

          但情況變得更糟了:我們?nèi)匀槐仨毩谐鏊凶詣拥闹?,而且字段順序還不能變(當(dāng)然,newtype 技術(shù)可以幫助我們,但這篇文章不是關(guān)于它的)。

          建造者模式可以拯救我們

          建造者是一個(gè)額外的結(jié)構(gòu),它提供了一個(gè)符合人體工程學(xué)的接口來設(shè)置值和構(gòu)建目標(biāo)結(jié)構(gòu)的方法。讓我們實(shí)現(xiàn) UserBuilder 以便幫助我們構(gòu)建 User:

          struct?UserBuilder?{
          ????email:?Option<String>,
          ????first_name:?Option<String>,
          ????last_name:?Option<String>
          }

          impl?UserBuilder?{
          ????fn?new()?->?Self?{
          ????????Self?{
          ????????????email:?None,
          ????????????first_name:?None,
          ????????????last_name:?None,
          ????????}
          ????}

          ????fn?email(mut?self,?email:?impl?Into<String>)?->?Self?{
          ????????self.email?=?Some(email.into());
          ????????self
          ????}

          ????fn?first_name(mut?self,?first_name:?impl?Into<String>)?->?Self?{
          ????????self.first_name?=?Some(first_name.into());
          ????????self
          ????}

          ????fn?last_name(mut?self,?last_name:?impl?Into<String>)?->?Self?{
          ????????self.last_name?=?Some(last_name.into());
          ????????self
          ????}

          ????fn?build(self)?->?User?{
          ????????let?Self?{?email,?first_name,?last_name?}?=?self;
          ????????User?{?email,?first_name,?last_name?}
          ????}
          }

          值得注意的點(diǎn):

          • 建造者類似于它構(gòu)建的目標(biāo)結(jié)構(gòu):UserBuilderUser 字段相同
          • 每個(gè)字段有一個(gè) setter 函數(shù):email, first_name, last_name
          • setter 函數(shù)第一個(gè)參數(shù)是一個(gè) builder(mut self),設(shè)置值,并將構(gòu)建器返回。這使得可以鏈?zhǔn)秸{(diào)用
          • new()創(chuàng)建具有預(yù)定義默認(rèn)值的建造者(在這種情況下,所有字段值都是None)
          • build() 構(gòu)建并返回目標(biāo)結(jié)構(gòu) User
          • 它與建造者模式直接無關(guān),但我們接收 impl Into 而不是 String 來更新 setter 的值。這使得我們的 API 更加靈活

          通常為了方便 User 會實(shí)現(xiàn) builder() 函數(shù),因此 ?UserBuilder 不必明確導(dǎo)入:

          impl?User?{
          ????fn?builder()?->?UserBuilder?{
          ????????UserBuilder::new()
          ????}
          }

          最終,通過建造者我們可以構(gòu)建相同的 User 結(jié)構(gòu)體實(shí)例:

          let?greyblake?=?User::builder()
          ????.email("[email protected]")
          ????.first_name("Sergey")
          ????.build();

          雖然它仍然比 Ruby 版本 User.new 代碼略多,但我們實(shí)現(xiàn)了目標(biāo):

          • 跳過不相關(guān)的字段并隱含使用默認(rèn)值
          • 相關(guān)字段及其值已明確闡明
          • 不再有類型噪音,對于 Option,不需要 ?Some(...)

          03 必填字段

          現(xiàn)在假設(shè) User 結(jié)構(gòu)體有必填字段:idemail,這是更接近現(xiàn)實(shí)生活中的例子:

          struct?User?{
          ????id:?String,
          ????email:?String,
          ????first_name:?Option,
          ????last_name:?Option,
          }

          Buidler 不能有關(guān)于 idemail 合理的默認(rèn)值,所以我們必須找到一種方法來傳遞它們。

          而在 Ruby 中,可以強(qiáng)制要求 idemail 必填,只需要在構(gòu)造函數(shù)中將其中的默認(rèn)值 nil 移除即可:

          class?User
          ??def?initialize(id:,?email:,?first_name:?nil,?last_name:?nil)
          ??#?...
          ??end
          end

          在 Rust 中,為了解決這個(gè)問題,我們可以調(diào)整建造者的構(gòu)造器以接收必填字段的值:

          struct?UserBuilder?{
          ????id:?String,
          ????email:?String,
          ????first_name:?Option<String>,
          ????last_name:?Option<String>,
          }

          impl?UserBuilder?{
          ????fn?new(id:?impl?Into<String>,?email:?impl?Into<String>)?->?Self?{
          ????????Self?{
          ????????????id:?id.into(),
          ????????????email:?email.into(),
          ????????????first_name:?None,
          ????????????last_name:?None,
          ????????}
          ????}

          ????fn?first_name(mut?self,?first_name:?impl?Into<String>)?->?Self?{
          ????????self.first_name?=?Some(first_name.into());
          ????????self
          ????}

          ????fn?last_name(mut?self,?last_name:?impl?Into<String>)?->?Self?{
          ????????self.last_name?=?Some(last_name.into());
          ????????self
          ????}

          ????fn?build(self)?->?User?{
          ????????let?Self?{?id,?email,?first_name,?last_name?}?=?self;
          ????????User?{?id,?email,?first_name,?last_name?}
          ????}
          }

          impl?User?{
          ????fn?builder(id:?impl?Into<String>,?email:?impl?Into<String>)?->?UserBuilder?{
          ????????UserBuilder::new(id,?email)
          ????}
          }

          這使我們能夠構(gòu)建一個(gè)用戶,確保始終指定 idemail

          let?greyblake?=?User::builder("13",?"[email protected]")
          ????.first_name("Sergey")
          ????.build();

          不幸的是,它給我們帶來了與本文開頭的建造者相同的問題:字段名稱沒有明確說明,很容易以錯(cuò)誤的順序傳遞參數(shù)。

          有沒有解決辦法呢?我們下篇文章見!

          原文鏈接:https://www.greyblake.com/blog/2021-10-19-builder-pattern-in-rust/

          完整建造者模式代碼:https://github.com/colin-kiegel/rust-derive-builder




          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標(biāo)準(zhǔn)庫》等。


          堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio


          瀏覽 158
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产女人操逼视屏免费播 | 天天久天操天天淫 | 欧美骚逼网站 | 亚洲第一精品在线观看 | 一区二区三区四区五区六区久久 |