中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

了解Go扁平化項目結(jié)構(gòu)

無需花時間試圖弄清楚如何將代碼分解為軟件包,而是采用扁平結(jié)構(gòu)的應(yīng)用程序會將所有.go文件放置在一個軟件包中。

創(chuàng)新互聯(lián)長期為上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為泗縣企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)泗縣網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
myapp/
  main.go
  server.go
  user.go
  lesson.go
  course.go

進(jìn)入Go時,幾乎每個人都從一個平面應(yīng)用程序結(jié)構(gòu)開始。 Go tour中的每個程序,Gophercises中的大多數(shù)練習(xí)以及許多其他早期的Go程序都沒有被分解成任何包裝。取而代之的是,我們只創(chuàng)建幾個.go文件,然后將所有代碼放入相同的(通常是main)包中。

起初,這聽起來很糟糕。代碼會很快變得笨拙嗎?如何將業(yè)務(wù)邏輯與UI渲染代碼分開?我如何找到正確的源文件?畢竟,我們使用軟件包的很大一部分原因是要分離關(guān)注點,同時使更容易快速地導(dǎo)航到正確的源文件。

相關(guān)學(xué)習(xí)推薦:Go語言教程

有效使用平面結(jié)構(gòu)

使用平面結(jié)構(gòu)時,您仍應(yīng)嘗試遵守編碼實踐。您將需要使用不同的.go文件分隔應(yīng)用程序的不同部分:

myapp /
  main.go#閱讀配置并在此處啟動您的應(yīng)用
  server.go#總體HTTP處理邏輯在這里
  user_handler.go#用戶http處理程序邏輯在這里
  user_store.go#用戶數(shù)據(jù)庫邏輯在這里
  # 等等...

全局變量仍然可能成為問題,因此您應(yīng)考慮將類型與方法配合使用,以使它們脫離代碼:

type Server struct {
  apiClient *someapi.Client
  router *some.Router
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  s.router.ServeHTTP(w, r)
}

而且您的main()函數(shù)可能仍應(yīng)在設(shè)置應(yīng)用程序之外刪除大多數(shù)邏輯:

//警告:此示例非常人為設(shè)計,甚至可能無法編譯。

type Config struct {
  SomeAPIKey     string
  Port           string
  EnableTheThing bool
}

func main() {
  var config Config
  config.SomeAPIKey = os.Getenv("SOMEAPI_KEY")
  config.Port = os.Getenv("MYAPP_PORT")
  if config.Port == "" {
    config.Port = "3000"
  }
  config.EnableTheThing = true

  if err := run(config); err != nil {
    log.Fatal(err)
  }
}

func run(config Config) error {
  server := myapp.Server{
    APIClient: someapi.NewClient(config.SomeAPIKey),
  }
  return http.ListenAndServe(":" + config.Port, server)
}

實際上,您實際上可以將基本上是平面結(jié)構(gòu)的代碼全部使用在一個軟件包中,并在單獨的main軟件包中定義命令。這將允許您使用常見的cmd子目錄模式:

myapp/
  cmd/
    web/
      # package main
      main.go
    cli/
      # package main
      main.go
  # package myapp
  server.go
  user_handler.go
  user_store.go
  ...

在此示例中,您的應(yīng)用程序基本上仍然是平坦的,但是您拔出了main軟件包是因為您有需要-例如可能需要使用同一核心應(yīng)用程序來支持兩個命令。

為什么要使用扁平結(jié)構(gòu)?

扁平結(jié)構(gòu)的主要好處不是將所有代碼都保存在一個目錄中,也不是那樣愚蠢的東西。這種結(jié)構(gòu)的核心好處是您可以不必?fù)?dān)心如何組織事物,而可以繼續(xù)解決您打算通過應(yīng)用程序解決的問題。

我絕對喜歡這個應(yīng)用程序結(jié)構(gòu)讓我回想起PHP的日子。當(dāng)我第一次學(xué)習(xí)編碼時,我開始使用隨機PHP文件,其邏輯與各種HTML混合在一起,這真是一團(tuán)糟。我并不是在建議我們以大型應(yīng)用程序的方式構(gòu)建-那樣會很糟糕-但是我并不擔(dān)心一切都應(yīng)該放在哪里,而是更加關(guān)注學(xué)習(xí)如何編寫代碼和解決我的特定問題。無論您是要了解應(yīng)用程序的需求,您的域還是一般的編碼方式,使用扁平結(jié)構(gòu)都可以使您更輕松地專注于學(xué)習(xí)和構(gòu)建。

這是正確的,因為我們可以不再擔(dān)心諸如“這種邏輯應(yīng)該去哪里?”之類的問題。因為如果我們犯了一個錯誤,很容易解決。如果它是一個函數(shù),我們可以將其移動到包中的任何新源文件中。如果它是錯誤類型的方法,我們可以創(chuàng)建兩個新類型并將邏輯與原始類型分開。有了這些,我們就不必?fù)?dān)心會遇到奇怪的周期性依賴問題,因為我們只有一個軟件包。

考慮平面結(jié)構(gòu)的另一個重要原因是,隨著應(yīng)用程序復(fù)雜性的提高,結(jié)構(gòu)的發(fā)展變得容易得多。當(dāng)您明顯可以從將代碼拆分到一個單獨的程序包中受益時,您通常需要做的就是將一些源文件移到一個子目錄中,更改其程序包,并更新任何引用以使用新的程序包前綴。例如,如果我們有SqlUser并決定從一個單獨的sql包中處理所有與數(shù)據(jù)庫相關(guān)的邏輯中受益,我們將更新所有引用以現(xiàn)在使用sql.User將類型移動到新軟件包后。我發(fā)現(xiàn),像MVC這樣的結(jié)構(gòu)在重構(gòu)方面更具挑戰(zhàn)性,盡管并非沒有其他編程語言那樣困難或困難。

扁平結(jié)構(gòu)對于通常太快無法創(chuàng)建包的初學(xué)者特別有用。我真的不能說為什么會發(fā)生這種現(xiàn)象,但是Go的新手喜歡創(chuàng)建大量的軟件包,這幾乎總是導(dǎo)致口吃(user.User),周期性依賴關(guān)系或其他一些問題。

在下一篇有關(guān)MVC的文章中,我們將探討這種創(chuàng)建過多包的現(xiàn)象如何使MVC在Go中顯得不可能的方法,盡管事實并非如此。

通過推遲創(chuàng)建新程序包的決定,直到我們的應(yīng)用程序增長一點并更好地了解它,發(fā)芽的Gophers犯此錯誤的可能性就大大降低了。

這也是為什么很多人會鼓勵開發(fā)人員避免過早將其代碼分解到微服務(wù)中的原因-您通常沒有足夠的知識來真正知道應(yīng)該和不應(yīng)該將哪些內(nèi)容分解為微服務(wù)以及搶先式微服務(wù)(I kinda希望能成為一句俗語)只會在將來帶來更多工作。

平坦的結(jié)構(gòu)并不全是陽光和彩虹

假裝使用扁平結(jié)構(gòu)沒有任何不利之處,這對我來說是不誠實的,所以我們也應(yīng)該討論這些。

對于初學(xué)者來說,扁平的結(jié)構(gòu)只能使您受益匪淺。它會工作一段時間(可能比您想象的更長),但是到某個時候,您的應(yīng)用程序?qū)⒆兊米銐驈?fù)雜,您需要開始分解它。使用平面結(jié)構(gòu)的好處是您可以推遲使用它,并且在分解時可能會更好地理解您的代碼。缺點是,您將需要花一些時間進(jìn)行重構(gòu),并且您可能(也許-但這很麻煩)發(fā)現(xiàn)自己已經(jīng)重構(gòu)為您想從任何地方開始的結(jié)構(gòu)。

使用平面結(jié)構(gòu)時,命名沖突有時也會很尷尬。例如,假設(shè)您想要在應(yīng)用程序中使用Course類型,但是在數(shù)據(jù)庫中表示課程的方式與在JSON中呈現(xiàn)課程的方式不同。一個快速的解決方案是創(chuàng)建兩種類型,但是由于它們都在同一個包中,因此每種類型都需要使用不同的名稱,并且可能最終以類似以下內(nèi)容的形式出現(xiàn):SqlCourseJsonCourse。這確實沒什么大不了的,但是有點令人遺憾的是我們最終得到了零類型,簡單地稱為Course

將代碼重構(gòu)為新程序包也不總是那么簡單。是的,這通常很容易,但是由于所有代碼都在一個包中,因此您有時可能會遇到天生具有周期性的代碼。例如,假設(shè)我們的課程是否具有在JSON響應(yīng)中始終以crs_開頭的ID,并且我們想以各種貨幣返回價格。我們可以創(chuàng)建一個JsonCourse來處理:

輸入JsonCourse struct {
  ID字符串`json:“ id”`
  價格結(jié)構(gòu){
    USD字符串`json:“ usd”`
  }`json:“價格”`
}

同時,SqlCourse僅需要存儲一個整數(shù)ID和一個以美分為單位的單一價格,我們可以使用各種貨幣對其進(jìn)行格式化。

type SqlCourse struct {
  ID    int
  Price int
}

現(xiàn)在我們需要一種將SqlCourse轉(zhuǎn)換為JsonCourse的方法,因此我們可以將其作為SqlCourse類型的方法:

func (sc SqlCourse) ToJson() (JsonCourse, error) {
  jsonCourse := JsonCourse{
    ID: fmt.Sprintf("crs_%v", sc.ID),
  }
  jsonCourse.Price.USD = Price: fmt.Sprintf("%d.%2d", sc.Price/100, sc.Price%100)
  return jsonCourse, nil
}

然后稍后我們可能需要一種方法來解析傳入的JSON并將其轉(zhuǎn)換為SQL等效項,因此我們將其添加到JsonCourse類型中作為另一種方法:

func (jc JsonCourse) ToSql() (SqlCourse, error) {
  var sqlCourse SqlCourse
  // JSON ID is "crs_123" and we convert to "123"
  // for SQL IDs
  id, err := strconv.Atoi(strings.TrimPrefix(jc.ID, "crs_"))
  if err != nil {
    // Note: %w is a Go 1.13 thing that I haven't really
    // tested out, so let me know if I'm using it wrong

相關(guān)學(xué)習(xí)推薦:編程視頻

分享標(biāo)題:了解Go扁平化項目結(jié)構(gòu)
文章源于:http://www.rwnh.cn/article12/cjoddc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、營銷型網(wǎng)站建設(shè)網(wǎng)站維護(hù)、電子商務(wù)、網(wǎng)站建設(shè)、用戶體驗

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)
上思县| 横峰县| 阳城县| 涞源县| 哈尔滨市| 永川市| 清水河县| 逊克县| 繁峙县| 辰溪县| 曲阳县| 安陆市| 武夷山市| 永泰县| 北京市| 北安市| 西吉县| 平安县| 桑日县| 长兴县| 宜昌市| 临洮县| 贵溪市| 青海省| 崇义县| 卢龙县| 通州区| 连州市| 丹巴县| 曲沃县| 旬阳县| 延川县| 碌曲县| 房产| 安国市| 太仓市| 达孜县| 樟树市| 呼图壁县| 泸溪县| 锦屏县|