1. 程式人生 > 實用技巧 >普里姆演算法 PHP寫法

普里姆演算法 PHP寫法

目錄

介面

在講解具體的介面之前,先看如下問題。

使用面向物件的方式,設計一個加減的計算器

程式碼如下:

package main

import "fmt"

//父類
type Operate struct {
    num1 int
    num2 int
}

//加法子類
type Add struct {
    Operate
}

//減法子類
type Sub struct {
    Operate
}

//加法子類的方法
func (a *Add) Result() int {
    return a.num1 + a.num2
}

//減法子類的方法
func (s *Sub) Result() int {
    return s.num1 - s.num2
}

//方法呼叫
func main0201() {
    //建立加法物件
    //var a Add
    //a.num1 = 10
    //a.num2 = 20
    //v := a.Result()
    //fmt.Println(v)

    //建立減法物件
    var s Sub
    s.num1 = 10
    s.num2 = 20
    v := s.Result()
    fmt.Println(v)
}

以上實現非常簡單,但是有個問題,在main( )函式中,當我們想使用減法操作時,建立減法類的物件,呼叫其對應的減法的方法。但是,有一天,系統需求發生了變化,要求使用加法,不再使用減法,那麼需要對main( )函式中的程式碼,做大量的修改。將原有的程式碼註釋掉,建立加法的類物件,呼叫其對應的加法的方法。有沒有一種方法,讓main( )函式,只修改很少的程式碼就可以解決該問題呢?有,要用到接下來給大家講解的介面的知識點。

1.什麼是介面

介面就是一種規範與標準,在生活中經常見介面,例如:膝上型電腦的USB介面,可以將任何廠商生產的滑鼠與鍵盤,與電腦進行連結。為什麼呢?原因就是,USB介面將規範和標準制定好後,各個生產廠商可以按照該標準生產滑鼠和鍵盤就可以了。

在程式開發中,介面只是規定了要做哪些事情,幹什麼。具體怎麼做,介面是不管的。這和生活中介面的案例也很相似,例如:USB介面,只是規定了標準,但是不關心具體滑鼠與鍵盤是怎樣按照標準生產的.

在企業開發中,如果一個專案比較龐大,那麼就需要一個能理清所有業務的架構師來定義一些主要的介面,這些介面告訴開發人員你需要實現那些功能。

2.介面定義

介面定義的語法如下:

//先定義介面  一般以er結尾  根據介面實現功能
type Humaner interface {
    //方法  方法的宣告
    sayhi()
}

怎樣具體實現介面中定義的方法呢?

type student11 struct {
    name  string
    age   int
    score int
}

func (s *student11)sayhi()  {
    fmt.Printf("大家好,我是%s,今年%d歲,我的成績%d分\n",s.name,s.age,s.score)
}

type teacher11 struct {
    name    string
    age     int
    subject string
}

func (t *teacher11)sayhi()  {
    fmt.Printf("大家好,我是%s,今年%d歲,我的學科是%s\n",t.name,t.age,t.subject)
}

具體的呼叫如下:

func main() {
    //介面是一種資料型別 可以接收滿足物件的資訊
    //介面是虛的  方法是實的
    //介面定義規則  方法實現規則
    //介面定義的規則  在方法中必須有定義的實現
    var h Humaner

    stu := student11{"小明",18,98}
    //stu.sayhi()
    //將物件資訊賦值給介面型別變數
    h = &stu
    h.sayhi()

    tea := teacher11{"老王",28,"物理"}
    //tea.sayhi()
    //將物件賦值給介面 必須滿足介面中的方法的宣告格式
    h = &tea
    h.sayhi()
}

只要類(結構體)實現對應的介面,那麼根據該類建立的物件,可以賦值給對應的介面型別。

介面的命名習慣以er結尾。

3.多型

介面有什麼好處呢?實現多型。

多型就是同一個介面,使用不同的例項而執行不同操作

所謂多型指的是多種表現形式,如下圖所示:

使用介面實現多型的方式如下:

package main

import "fmt"

//先定義介面  一般以er結尾  根據介面實現功能
type Humaner1 interface {
    //方法  方法的宣告
    sayhi()

}


type student12 struct {
    name  string
    age   int
    score int
}

func (s *student12)sayhi()  {
    fmt.Printf("大家好,我是%s,今年%d歲,我的成績%d分\n",s.name,s.age,s.score)
}

type teacher12 struct {
    name    string
    age     int
    subject string
}

func (t *teacher12)sayhi()  {
    fmt.Printf("大家好,我是%s,今年%d歲,我的學科是%s\n",t.name,t.age,t.subject)
}

//多型的實現
//將介面作為函式引數  實現多型
func sayhello(h Humaner1)  {
    h.sayhi()
}

func main() {

    stu := student12{"小明",18,98}
    //呼叫多型函式
    sayhello(&stu)

    tea := teacher12{"老王",28,"Go"}
    sayhello(&tea)
}

關於介面的定義,以及使用介面實現多型,大家都比較熟悉了,但是多型有什麼好處呢?現在還是以開始提出的計算器案例給大家講解一下,在開始我們已經實現了一個加減功能的計算器,但是有同學感覺太麻煩了,因為實現加法,就要定義加法操作的類(結構體),實現減法就要定義減法的類(結構體),所以該同學實現了一個比較簡單的加減法的計算器,如下所示:

1.使用面向物件的思想實現一個加減功能的計算器,可能有同學感覺非常簡單,程式碼如下:

我們定義了一個類(結構體),然後為該類建立了一個方法,封裝了整個計算器功能,以後要使用直接使用該類(結構體)建立物件就可以了。這就是面向物件總的封裝性。

也就是說,當你寫完這個計算器後,交給你的同事,你的同事要用,直接建立物件,然後呼叫GetResult()方法就可以,根本不需要關心該方法是怎樣實現的.這不是我們前面在講解面向物件概念時說到的,找個物件來幹活嗎?不需要自己去實現該功能。

2.大家仔細觀察上面的程式碼,有什麼問題嗎?

現在讓你在改計算器中,再增加一個功能,例如乘法,應該怎麼辦呢?你可能會說很簡單啊,直接在GetResult( )方法的switch中新增一個case分支就可以了。

問題是:在這個過程中,如果你不小心將加法修改成了減法怎麼辦?或者說,對加法運算的規則做了修改怎麼辦?舉例子說明:

你可以把該程式方法想象成公司中的薪資管理系統。如果公司決定對薪資的運算規則做修改,由於所有的運算規則都在Operation類中的GetResult()方法中,所以公司只能將該類的程式碼全部給你,你才能進行修改。這時,你一看自己作為開發人員工資這麼低,心想“TMD,老子累死累活才給這麼點工資,這下有機會了”。直接在自己工資後面加了3000

numA+numB+3000

所以說,我們應該將加減等運算分開,不應該全部糅合在一起,這樣你修改加的時候,不會影響其它的運算規則:

具體實現如下:

現在已經將各個操作分開了,並且這裡我們還定義了一個父類(結構體),將公共的成員放在該父類中。如果現在要修改某項運算規則,只需將對應的類和方法發給你,進行修改就可以了。

這裡的實現雖然將各個運算分開了,但是與我們第一次實現的還是有點區別。我們第一次實現的加減計算器也是將各個運算分開了,但是沒有定義介面。那麼該介面的意義是什麼呢?繼續看下面的問題。

3:現在怎樣呼叫呢?

這就是我們一開始給大家提出的問題,如果呼叫的時候,直接建立加法操作的物件,呼叫對應的方法,那麼後期要改成減法呢?需要做大量的修改,所以問題解決的方法如下:

建立了一個類OperationFactory,在改類中添加了一個方法CreateOption( )負責建立物件,如果輸入的是“+”,建立

OperationAdd的物件,然後呼叫OperationWho( )方法,將物件的地址傳遞到該方法中,所以變數i指的就是OperationAdd,接下來在呼叫GetResult( )方法,實際上呼叫的是OperationAdd類實現的GetResult( )方法。

同理如果傳遞過來的是“-”,流程也是一樣的。

所以,通過該程式,大家能夠體會出多型帶來的好處。

4:最後呼叫

這時會發現呼叫,非常簡單,如果現在想計算加法,只要將”-”,修改成”+”就可以。也就是說,除去了main( )函式與具體運算類的依賴。

當然程式經過這樣設計以後:如果現在修改加法的運算規則,只需要修改OperationAdd類中對應的方法,

不需要關心其它的類,如果現在要增加“乘法” 功能,應該怎樣進行修改呢?第一:定義乘法的類,完成乘法運算。

第二:在OperationFactory類中CrateOption( )方法中新增相應的分支。但是這樣做並不會影響到其它的任何運算。

大家可以自己嘗試實現“乘法”與“除法”的運算。

在使用面向物件思想解決問題時,一定要先分析,定義哪些類,哪些介面,哪些方法。把這些分析定義出來,然後在考慮具體實現。

最後完整程式碼如下:

package main

import "fmt"

//定義介面
type Opter interface {
    //方法宣告
    Result() int
}

//父類
type Operate struct {
    num1 int
    num2 int
}
//加法子類
type Add struct {
    Operate
}

//加法子類的方法
func (a *Add) Result() int {
    return a.num1 + a.num2
}

//減法子類
type Sub struct {
    Operate
}

//減法子類的方法
func (s *Sub) Result() int {
    return s.num1 - s.num2
}

//建立一個類負責物件建立
//工廠類
type Factory struct {
}

func (f *Factory) Result(num1 int, num2 int, ch string) {
    switch ch {
    case "+":
        var a Add
        a.num1 = num1
        a.num2 = num2
        Result(&a)
    case "-":
        var s Sub
        s.num1 = num1
        s.num2 = num2
        Result(&s)

    }
}

//通過設計模式呼叫
func main() {
    //建立工廠物件
    var f Factory
    f.Result(10, 20, "+")
}

下面我們將介面其它的知識點再給大家說一下:

4.介面繼承與轉換(瞭解)

介面也可以實現繼承:

package main

import "fmt"

//先定義介面  一般以er結尾  根據介面實現功能
type Humaner2 interface {   //子集
    //方法  方法的宣告
    sayhi()

}

type Personer interface {  //超集
    Humaner2   //繼承sayhi()

    sing(string)
}

type student13 struct {
    name  string
    age   int
    score int
}

func (s *student13)sayhi()  {
    fmt.Printf("大家好,我是%s,今年%d歲,我的成績%d分\n",s.name,s.age,s.score)
}

func (s *student13)sing(name string)  {
    fmt.Println("我為大家唱首歌",name)
}

func main() {
    //介面型別變數定義
    var h Humaner2
    var stu student13 = student13{"小吳",18,59}
    h = &stu
    h.sayhi()

    //介面型別變數定義
    var p Personer
    p = &stu
    p.sayhi()
    p.sing("大碗麵")
}

介面繼承後,可以實現“超集”介面轉換“子集”介面,程式碼如下:

package main

import "fmt"

//先定義介面  一般以er結尾  根據介面實現功能
type Humaner2 interface {   //子集
    //方法  方法的宣告
    sayhi()

}

type Personer interface {  //超集
    Humaner2   //繼承sayhi()

    sing(string)
}

type student13 struct {
    name  string
    age   int
    score int
}

func (s *student13)sayhi()  {
    fmt.Printf("大家好,我是%s,今年%d歲,我的成績%d分\n",s.name,s.age,s.score)
}

func (s *student13)sing(name string)  {
    fmt.Println("我為大家唱首歌",name)
}

func main()  {
    //介面型別變數定義
    var h Humaner2  //子集
    var p Personer    //超集
    var stu student13 = student13{"小吳",18,59}

    p = &stu
    //將一個介面賦值給另一個介面
    //超集中包含所有子集的方法
    h = p  //ok

    h.sayhi()

    //子集不包含超集
    //不能將子集賦值給超集
    //p = h  //err
    //p.sayhi()
    //p.sing("大碗麵")
}

5.空介面

空介面(interface{})不包含任何的方法,正因為如此,所有的型別都實現了空介面,因此空介面可以儲存任意型別的數值。

例如:

var i interface{}
//介面型別可以接收任意型別的資料
//fmt.Println(i)
fmt.Printf("%T\n",i)
i = 10
fmt.Println(i)
fmt.Printf("%T\n",i)

當函式可以接受任意的物件例項時,我們會將其宣告為interface{},最典型的例子是標準庫fmt中PrintXXX系列的函式,例如:

func Printf(fmt string, args ...interface{})
func Println(args ...interface{})

如果自己定義函式,可以如下:

func Test(arg ...interface{}) {

}

Test( )函式可以接收任意個數,任意型別的引數。