<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>

          Go 經(jīng)典入門系列 28:多態(tài)

          共 8280字,需瀏覽 17分鐘

           ·

          2020-12-30 21:52

          點(diǎn)擊上方藍(lán)色“Go語(yǔ)言中文網(wǎng)”關(guān)注,每天一起學(xué) Go

          歡迎來(lái)到 Golang 系列教程[1]的第 28 篇。

          Go 通過接口[2]來(lái)實(shí)現(xiàn)多態(tài)。我們已經(jīng)討論過,在 Go 語(yǔ)言中,我們是隱式地實(shí)現(xiàn)接口。一個(gè)類型如果定義了接口所聲明的全部方法[3],那它就實(shí)現(xiàn)了該接口。現(xiàn)在我們來(lái)看看,利用接口,Go 是如何實(shí)現(xiàn)多態(tài)的。

          使用接口實(shí)現(xiàn)多態(tài)

          一個(gè)類型如果定義了接口的所有方法,那它就隱式地實(shí)現(xiàn)了該接口。

          所有實(shí)現(xiàn)了接口的類型,都可以把它的值保存在一個(gè)接口類型的變量中。在 Go 中,我們使用接口的這種特性來(lái)實(shí)現(xiàn)多態(tài)

          通過一個(gè)程序我們來(lái)理解 Go 語(yǔ)言的多態(tài),它會(huì)計(jì)算一個(gè)組織機(jī)構(gòu)的凈收益。為了簡(jiǎn)單起見,我們假設(shè)這個(gè)虛構(gòu)的組織所獲得的收入來(lái)源于兩個(gè)項(xiàng)目:fixed billingtime and material。該組織的凈收益等于這兩個(gè)項(xiàng)目的收入總和。同樣為了簡(jiǎn)單起見,我們假設(shè)貨幣單位是美元,而無(wú)需處理美分。因此貨幣只需簡(jiǎn)單地用 int 來(lái)表示。(我建議閱讀 https://forum.golangbridge.org/t/what-is-the-proper-golang-equivalent-to-decimal-when-dealing-with-money/413 上的文章,學(xué)習(xí)如何表示美分。感謝 Andreas Matuschek 在評(píng)論區(qū)指出這一點(diǎn)。)

          我們首先定義一個(gè)接口 Income

          type?Income?interface?{
          ????calculate()?int
          ????source()?string
          }

          上面定義了接口 Interface,它包含了兩個(gè)方法:calculate() 計(jì)算并返回項(xiàng)目的收入,而 source() 返回項(xiàng)目名稱。

          下面我們定義一個(gè)表示 FixedBilling 項(xiàng)目的結(jié)構(gòu)體類型。

          type?FixedBilling?struct?{
          ????projectName?string
          ????biddedAmount?int
          }

          項(xiàng)目 FixedBillin 有兩個(gè)字段:projectName 表示項(xiàng)目名稱,而 biddedAmount 表示組織向該項(xiàng)目投標(biāo)的金額。

          TimeAndMaterial 結(jié)構(gòu)體用于表示項(xiàng)目 Time and Material。

          type?TimeAndMaterial?struct?{
          ????projectName?string
          ????noOfHours??int
          ????hourlyRate?int
          }

          結(jié)構(gòu)體 TimeAndMaterial 擁有三個(gè)字段名:projectNamenoOfHourshourlyRate

          下一步我們給這些結(jié)構(gòu)體類型定義方法,計(jì)算并返回實(shí)際收入和項(xiàng)目名稱。

          func?(fb?FixedBilling)?calculate()?int?{
          ????return?fb.biddedAmount
          }

          func?(fb?FixedBilling)?source()?string?{
          ????return?fb.projectName
          }

          func?(tm?TimeAndMaterial)?calculate()?int?{
          ????return?tm.noOfHours?*?tm.hourlyRate
          }

          func?(tm?TimeAndMaterial)?source()?string?{
          ????return?tm.projectName
          }

          在項(xiàng)目 FixedBilling 里面,收入就是項(xiàng)目的投標(biāo)金額。因此我們返回 FixedBilling 類型的 calculate() 方法。

          而在項(xiàng)目 TimeAndMaterial 里面,收入等于 noOfHourshourlyRate 的乘積,作為 TimeAndMaterial 類型的 calculate() 方法的返回值。

          我們還通過 source() 方法返回了表示收入來(lái)源的項(xiàng)目名稱。

          由于 FixedBillingTimeAndMaterial 兩個(gè)結(jié)構(gòu)體都定義了 Income 接口的兩個(gè)方法:calculate()source(),因此這兩個(gè)結(jié)構(gòu)體都實(shí)現(xiàn)了 Income 接口。

          我們來(lái)聲明一個(gè) calculateNetIncome 函數(shù),用來(lái)計(jì)算并打印總收入。

          func?calculateNetIncome(ic?[]Income)?{
          ????var?netincome?int?=?0
          ????for?_,?income?:=?range?ic?{
          ????????fmt.Printf("Income?From?%s?=?$%d\n",?income.source(),?income.calculate())
          ????????netincome?+=?income.calculate()
          ????}
          ????fmt.Printf("Net?income?of?organisation?=?$%d",?netincome)
          }

          上面的函數(shù)[4]接收一個(gè) Income 接口類型的切片[5]作為參數(shù)。該函數(shù)會(huì)遍歷這個(gè)接口切片,并依個(gè)調(diào)用 calculate() 方法,計(jì)算出總收入。該函數(shù)同樣也會(huì)通過調(diào)用 source() 顯示收入來(lái)源。根據(jù) Income 接口的具體類型,程序會(huì)調(diào)用不同的 calculate()source() 方法。于是,我們?cè)?calculateNetIncome 函數(shù)中就實(shí)現(xiàn)了多態(tài)。

          如果在該組織以后增加了新的收入來(lái)源,calculateNetIncome 無(wú)需修改一行代碼,就可以正確地計(jì)算總收入了。:)

          最后就剩下這個(gè)程序的 main 函數(shù)了。

          func?main()?{
          ????project1?:=?FixedBilling{projectName:?"Project?1",?biddedAmount:?5000}
          ????project2?:=?FixedBilling{projectName:?"Project?2",?biddedAmount:?10000}
          ????project3?:=?TimeAndMaterial{projectName:?"Project?3",?noOfHours:?160,?hourlyRate:?25}
          ????incomeStreams?:=?[]Income{project1,?project2,?project3}
          ????calculateNetIncome(incomeStreams)
          }

          在上面的 main 函數(shù)中,我們創(chuàng)建了三個(gè)項(xiàng)目,有兩個(gè)是 FixedBilling 類型,一個(gè)是 TimeAndMaterial 類型。接著我們創(chuàng)建了一個(gè) Income 類型的切片,存放了這三個(gè)項(xiàng)目。由于這三個(gè)項(xiàng)目都實(shí)現(xiàn)了 Interface 接口,因此可以把這三個(gè)項(xiàng)目放入 Income 切片。最后我們將該切片作為參數(shù),調(diào)用了 calculateNetIncome 函數(shù),顯示了項(xiàng)目不同的收益和收入來(lái)源。

          以下完整的代碼供你參考。

          package?main

          import?(
          ????"fmt"
          )

          type?Income?interface?{
          ????calculate()?int
          ????source()?string
          }

          type?FixedBilling?struct?{
          ????projectName?string
          ????biddedAmount?int
          }

          type?TimeAndMaterial?struct?{
          ????projectName?string
          ????noOfHours??int
          ????hourlyRate?int
          }

          func?(fb?FixedBilling)?calculate()?int?{
          ????return?fb.biddedAmount
          }

          func?(fb?FixedBilling)?source()?string?{
          ????return?fb.projectName
          }

          func?(tm?TimeAndMaterial)?calculate()?int?{
          ????return?tm.noOfHours?*?tm.hourlyRate
          }

          func?(tm?TimeAndMaterial)?source()?string?{
          ????return?tm.projectName
          }

          func?calculateNetIncome(ic?[]Income)?{
          ????var?netincome?int?=?0
          ????for?_,?income?:=?range?ic?{
          ????????fmt.Printf("Income?From?%s?=?$%d\n",?income.source(),?income.calculate())
          ????????netincome?+=?income.calculate()
          ????}
          ????fmt.Printf("Net?income?of?organisation?=?$%d",?netincome)
          }

          func?main()?{
          ????project1?:=?FixedBilling{projectName:?"Project?1",?biddedAmount:?5000}
          ????project2?:=?FixedBilling{projectName:?"Project?2",?biddedAmount:?10000}
          ????project3?:=?TimeAndMaterial{projectName:?"Project?3",?noOfHours:?160,?hourlyRate:?25}
          ????incomeStreams?:=?[]Income{project1,?project2,?project3}
          ????calculateNetIncome(incomeStreams)
          }

          在 playground 上運(yùn)行[6]

          該程序會(huì)輸出:

          Income?From?Project?1?=?$5000
          Income?From?Project?2?=?$10000
          Income?From?Project?3?=?$4000
          Net?income?of?organisation?=?$19000

          新增收益流

          假設(shè)前面的組織通過廣告業(yè)務(wù),建立了一個(gè)新的收益流(Income Stream)。我們可以看到添加它非常簡(jiǎn)單,并且計(jì)算總收益也很容易,我們無(wú)需對(duì) calculateNetIncome 函數(shù)進(jìn)行任何修改。這就是多態(tài)的好處。

          我們首先定義 Advertisement 類型,并在 Advertisement 類型中定義 calculate()source() 方法。

          type?Advertisement?struct?{
          ????adName?????string
          ????CPC????????int
          ????noOfClicks?int
          }

          func?(a?Advertisement)?calculate()?int?{
          ????return?a.CPC?*?a.noOfClicks
          }

          func?(a?Advertisement)?source()?string?{
          ????return?a.adName
          }

          Advertisement 類型有三個(gè)字段,分別是 adNameCPC(每次點(diǎn)擊成本)和 noOfClicks(點(diǎn)擊次數(shù))。廣告的總收益等于 CPCnoOfClicks 的乘積。

          現(xiàn)在我們稍微修改一下 main 函數(shù),把新的收益流添加進(jìn)來(lái)。

          func?main()?{
          ????project1?:=?FixedBilling{projectName:?"Project?1",?biddedAmount:?5000}
          ????project2?:=?FixedBilling{projectName:?"Project?2",?biddedAmount:?10000}
          ????project3?:=?TimeAndMaterial{projectName:?"Project?3",?noOfHours:?160,?hourlyRate:?25}
          ????bannerAd?:=?Advertisement{adName:?"Banner?Ad",?CPC:?2,?noOfClicks:?500}
          ????popupAd?:=?Advertisement{adName:?"Popup?Ad",?CPC:?5,?noOfClicks:?750}
          ????incomeStreams?:=?[]Income{project1,?project2,?project3,?bannerAd,?popupAd}
          ????calculateNetIncome(incomeStreams)
          }

          我們創(chuàng)建了兩個(gè)廣告項(xiàng)目,即 bannerAdpopupAdincomeStream 切片包含了這兩個(gè)創(chuàng)建的廣告項(xiàng)目。

          package?main

          import?(
          ????"fmt"
          )

          type?Income?interface?{
          ????calculate()?int
          ????source()?string
          }

          type?FixedBilling?struct?{
          ????projectName??string
          ????biddedAmount?int
          }

          type?TimeAndMaterial?struct?{
          ????projectName?string
          ????noOfHours???int
          ????hourlyRate??int
          }

          type?Advertisement?struct?{
          ????adName?????string
          ????CPC????????int
          ????noOfClicks?int
          }

          func?(fb?FixedBilling)?calculate()?int?{
          ????return?fb.biddedAmount
          }

          func?(fb?FixedBilling)?source()?string?{
          ????return?fb.projectName
          }

          func?(tm?TimeAndMaterial)?calculate()?int?{
          ????return?tm.noOfHours?*?tm.hourlyRate
          }

          func?(tm?TimeAndMaterial)?source()?string?{
          ????return?tm.projectName
          }

          func?(a?Advertisement)?calculate()?int?{
          ????return?a.CPC?*?a.noOfClicks
          }

          func?(a?Advertisement)?source()?string?{
          ????return?a.adName
          }
          func?calculateNetIncome(ic?[]Income)?{
          ????var?netincome?int?=?0
          ????for?_,?income?:=?range?ic?{
          ????????fmt.Printf("Income?From?%s?=?$%d\n",?income.source(),?income.calculate())
          ????????netincome?+=?income.calculate()
          ????}
          ????fmt.Printf("Net?income?of?organisation?=?$%d",?netincome)
          }

          func?main()?{
          ????project1?:=?FixedBilling{projectName:?"Project?1",?biddedAmount:?5000}
          ????project2?:=?FixedBilling{projectName:?"Project?2",?biddedAmount:?10000}
          ????project3?:=?TimeAndMaterial{projectName:?"Project?3",?noOfHours:?160,?hourlyRate:?25}
          ????bannerAd?:=?Advertisement{adName:?"Banner?Ad",?CPC:?2,?noOfClicks:?500}
          ????popupAd?:=?Advertisement{adName:?"Popup?Ad",?CPC:?5,?noOfClicks:?750}
          ????incomeStreams?:=?[]Income{project1,?project2,?project3,?bannerAd,?popupAd}
          ????calculateNetIncome(incomeStreams)
          }

          在 playground 中運(yùn)行[7]

          上面程序會(huì)輸出:

          Income?From?Project?1?=?$5000
          Income?From?Project?2?=?$10000
          Income?From?Project?3?=?$4000
          Income?From?Banner?Ad?=?$1000
          Income?From?Popup?Ad?=?$3750
          Net?income?of?organisation?=?$23750

          你會(huì)發(fā)現(xiàn),盡管我們新增了收益流,但卻完全沒有修改 calculateNetIncome 函數(shù)。這就是多態(tài)帶來(lái)的好處。由于新的 Advertisement 同樣實(shí)現(xiàn)了 Income 接口,所以我們能夠向 incomeStreams 切片添加 AdvertisementcalculateNetIncome 無(wú)需修改,因?yàn)樗軌蛘{(diào)用 Advertisement 類型的 calculate()source() 方法。

          本教程到此結(jié)束。祝你愉快。

          上一教程 - 組合取代繼承

          下一教程 - Defer[8]


          via: https://golangbot.com/polymorphism/

          作者:Nick Coghlan[9]譯者:Noluye[10]校對(duì):polaris1119[11]

          本文由 GCTT[12] 原創(chuàng)編譯,Go 中文網(wǎng)[13] 榮譽(yù)推出

          參考資料

          [1]

          Golang 系列教程: https://studygolang.com/subject/2

          [2]

          接口: https://studygolang.com/articles/12266

          [3]

          方法: https://studygolang.com/articles/12264

          [4]

          函數(shù): https://studygolang.com/articles/11892

          [5]

          切片: https://studygolang.com/articles/12121

          [6]

          在 playground 上運(yùn)行: https://play.golang.org/p/UClAagvLFT

          [7]

          在 playground 中運(yùn)行: https://play.golang.org/p/BYRYGjSxFN

          [8]

          Defer: https://studygolang.com/articles/12719

          [9]

          Nick Coghlan: https://golangbot.com/about/

          [10]

          Noluye: https://github.com/Noluye

          [11]

          polaris1119: https://github.com/polaris1119

          [12]

          GCTT: https://github.com/studygolang/GCTT

          [13]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  北条麻妃在线一区二区三区免费 | 婷婷色综合淫淫网 | 国产操逼www | 顶级欧美AAAAAA特级欧美AAAAAA | 欧美三级,美国一级 |