1. 程式人生 > >golang教程之多型性-Go面向物件

golang教程之多型性-Go面向物件

文章目錄

多型性-Go面向物件

https://golangbot.com/polymorphism/

Go中的多型性是在介面的幫助下實現的。正如我們已經討論過的,介面可以在Go中隱式實現。如果型別為介面中宣告的所有方法提供定義,則型別實現介面。讓我們看看在介面的幫助下如何在Go中實現多型性。

使用介面的多型性

任何定義介面所有方法的型別都被稱為隱式實現該介面。

介面型別的變數可以包含實現介面的任何值。介面的這個屬性用於在Go中實現多型。

讓我們在計算組織淨收入的程式的幫助下理解Go中的多型性。為簡單起見,我們假設這個想象中的組織有兩種專案的收入,即固定賬單,時間和材料。該組織的淨收入按這些專案的收入總和計算。為了簡化本教程,我們假設貨幣是美元,我們不會處理美分。它將使用int表示。 (我建議閱讀https://forum.golangbridge.org/t/what-is-the-proper-golang-equivalent-to-decimal-when-dealing-with-money/413以瞭解如何代表美分。)

我們首先定義一個 Income介面。

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

上面定義的Income介面包含兩個方法calculate(),它計算並返回source和source()的收入,返回source的名稱。

接下來讓我們為FixedBilling專案型別定義一個結構。

type FixedBilling struct {  
    projectName string
    biddedAmount int
}

FixedBilling專案有兩個欄位projectName,表示專案的名稱,biddedAmount是組織為專案出價的金額。

TimeAndMaterial結構將表示時間和材料型別的專案。

type TimeAndMaterial struct {  
    projectName string
    noOfHours  int
    hourlyRate int
}

TimeAndMaterial結構有三個欄位名稱projectNamenoOfHourshourlyRate

下一步是定義這些結構型別的方法,這些方法計算並返回實際收入和收入來源。

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
}

FixedBilling專案的情況下,收入就是專案的投標金額。 因此我們從FixedBilling型別的calculate()方法返回它。

對於TimeAndMaterial專案,收入是noOfHourshourlyRate的乘積。 此值從calculate()方法返回,接收器型別為TimeAndMaterial

我們將專案的名稱作為source()方法的收入來源返回。

由於FixedBillingTimeAndMaterial結構都為Income介面的calculate()source()方法提供了定義,因此兩個結構都實現了Income介面。

讓我們宣告calculateNetIncome函式,它將計算並列印總收入。

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

上面的calculateNetIncome函式接受切片Income介面作為引數。 它通過迭代切片並在每個專案上呼叫calculate()方法來計算總收入。 它還通過呼叫source()方法顯示收入來源。 根據Income介面的具體型別,將呼叫不同的calculate()source()方法。 我們在calculateNetIncome函式中實現了多型性。

在未來,如果組織添加了一種新的收入來源,這個功能仍然可以正確計算總收入而無需更改一行程式碼。

該程式中唯一剩下的部分是主函式。

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函式中,我們建立了三個專案,兩個型別為FixedBilling,另一個型別為TimeAndMaterial。 接下來,我們使用這3個專案建立一個型別為Income的切片。 由於這些專案中的每一個都實現了收入介面,因此可以將所有三個專案新增到收入型別的切片中。 最後,我們用這個切片呼叫calculateNetIncome函式,它將顯示各種收入來源和它們的收入。

這是完整的程式供您參考。

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

該程式將輸出

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

在上述計劃中新增新的收入流

假設該組織通過廣告找到了新的收入來源。 讓我們看看新增這個新的收入流並計算總收入是多麼簡單,而不對calculateNetIncome函式進行任何更改。 由於多型性,這成為可能。

讓我們首先在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 型別有三個欄位:adNameCPC(每次點選費用)和noOfClicks(點選次數)。 廣告的總收入是CPCnoOfClicks的產品。

讓我們稍微修改一下主函式,以包含這個新的收入流。

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

我們建立了兩個廣告,即bannerAdpopupAdincomeStreams切片包含我們剛剛建立的兩個廣告。

這是新增廣告後的完整程式。

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

以上程式將輸出,

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  

您會注意到雖然我們添加了新的收入流,但我們沒有對calculateNetIncome函式進行任何更改。 它只是因為多型性而起作用。 由於新的Advertisement型別也實現了Income介面,我們可以將它新增到incomeStreams切片中。calculateNetIncome函式也沒有任何變化,因為它能夠呼叫Advertisement型別的calculate()source()方法。