原文連結:Go for PHP Developers: Structs vsClasses
與現代PHP不同,Go通過結構的使用以不同的方式處理我們與應用程式中的型別互動的方式。事實上,它適合自己更原始,就像它的祖父C。
然而,Go確實實現了類似類的概念,從局外人的角度來看,可能會誤認為我們在PHP中看到的相同構造。
例如:Structs。
Go中的結構「Struct」是一種定義資料型別的方法,資料本身具有屬性。這些屬性有自己的型別,也可以是結構。
還有一種定義匿名屬性的方法,可以通過父結構“繼承”匿名屬性資料,方法等來訪問它。
定義我們的結構和類
讓我們看看PHP中的一個類,然後是Go中的一個結構,它實現了相同的功能:
PHP
<?php
namespace App;
class Animal
{
public $name;
public $height;
public $weight;
public function __construct($name, $height, $weight)
{
$this->name = $name;
$this->height = $height;
$this->weight = $weight;
}
public function getInfo()
{
return [
'name' => $this->name,
'height' => $this->height,
'weight' => $this->weight,
];
}
}
Go
package main
type Animal struct {
Name string
Height uint
Weight uint
}
func NewAnimal(name string, height, weight uint) Animal {
return Animal{
Name: name,
Height: height,
Weight: weight,
}
}
func (a *Animal) GetInfo() map[string]interface{} {
return map[string]interface{}{
"name": a.Name,
"height": a.Height,
"weight": a.Weight,
}
}
因為我們不處理Go中的類,所以沒有建構函式。因此我們定義了一個函式,它建立一個新版本的結構,分配我們傳入的引數值,應用程式基本相同,只是底層實現的方法已經改變了。
使用它們
說實話,除了語法上的不同之外,我們可以用類似的方式對待它們。一旦我們使用提供的型別建立了一個新物件,我們就可以幾乎相同地與它們進行互動。
PHP
$animal = new Animal("Tiger", 100, 200);
var_dump($animal->getInfo());
Go
var animal Animal
animal = NewAnimal("Tiger", 100, 200)
fmt.Println(animal.GetInfo())
您可能已經注意到Go示例中的函式定義具有名稱的大寫首字母;這表示該函式是公共的,並且在包之外可用;類似 PHP 中的關鍵字 public
如果我們將它設定與PHP 一樣私有的話,將其首字母設為小寫的話,它將無法在 main
包之外使用。
這是一個內建在Go中的自動作用域機制,也適用於函式外部包中定義的變數;您通常不會在包含類的PHP檔案中找到,但在Go中完全可以接受。
為我們的 Animal
定義介面
我們可能希望將任何結構或例項視為可以傳遞的型別契約,從而允許我們使用相同的介面提供多個不同的實現。
假設我們知道 getInfo
是我們 Animal
介面的定義方法,所以我們希望任何像 Animal
一樣的東西也提供這個,我們應該為此建立一個介面並在兩種情況下實現它。讓我們呼叫我們的介面 AnimalContract
。
我們可以這樣子做
在這些示例中,我們假設包裝和程式碼佈局的慣用方法。在PHP中,通常將類和介面作為自己的檔案,使其明視訊記憶體在於名稱空間中。為了簡單起見,Go中的小應用程式傾向於堅持主程式包,在這種情況下,假設所有程式碼都駐留在一個 main.go
中。
PHP
<?php
namespace App;
interface AnimalContract
{
public function getInfo();
}
我們要這樣實現它:
<?php
namespace App;
use App\AnimalContract;
class Animal implements AnimalContract;
{
// Rest of class...
}
Go
package main
type AnimalContract interface {
GetInfo() map[string]interface{}
}
type Animal struct {
// Struct def...
}
func (a *Animal) GetInfo() map[string]interface{} {
// Method body...
}
在Go實現中,我們不需要明確定義我們對介面的使用。Go很聰明,如果我們定義一個定義該介面的結構,它將隱式繼承它。
我們現在可以在應用程式的其他部分使用此介面,以確保我們收到提供此介面定義的內容。
例子
PHP
<?php
namespace App;
use App\Animal;
use App\AnimalContract;
function getAnimalInfo(AnimalContract $animal)
{
return $animal->getInfo();
}
$info = getAnimalInfo(new Animal("Tiger", 100, 200));
var_dump($info);
在這裡我們可以看到我們可以在函式定義中指定契約型別,允許我們傳遞繼承此介面的任何內容。如果您希望具有靈活性和擺動空間來交換功能的實現而不必導致重大更改,則非常有用。介面卡模式大量使用它,是一種簡單的方法來製作即插即用風格的應用程式,如果您正在開發一個開源庫或框架,那就太棒了。
現在我們將看到在 Go 中的做法並非完全不同。
Go
package main
type AnimalContract interface {
GetInfo() map[string]interface{}
}
type Animal struct {
// Struct def...
}
func (a *Animal) GetInfo() map[string]interface{} {
// Func def...
}
func GetAnimalInfo(animal AnimalContract) map[string]interface{} {
return animal.GetInfo();
}
我們可以在這裡看到,我們只需要指定介面型別名稱而不是結構。
擴充套件 Animals
我們可能會發現自己需要我們的 Animal
型別為我們提供的所有功能和介面,我們需要為我們的用例更改一些內容。也許我們需要一個特定於我們新型別的額外屬性,然後,再編寫一個新方法?
這是類擴充套件或嵌入式結構發揮作用的地方。兩種不同的實現和概念,但具有相似的用例。
讓我們建立另一種利用現有 Animal 定義的型別,但是在它的基礎上構建:
<?php
namespace App;
use App\Animal;
class Tiger extends Animal
{
public function __construct($height, $width)
{
parent::__construct("Tiger", $height, $width);
}
public function roar()
{
echo 'ROOARRR';
}
}
Go
// rest of main.go... & fmt import
type Tiger struct {
Animal
}
func (t *Tiger) Roar() {
fmt.Println("Roar");
}
在這兩種情況下,我們都新增了一個輸出一些文字的 Roar
方法;讓我們看看我們如何建立這些物件的區別。
PHP
<?php
use App\Tiger;
$tiger = new Tiger(200, 300);
$tiger->roar();
getAnimalInfo($tiger);
在PHP示例中,我們建立了一個新的Tiger
,根據我們的定義,我們不需要傳遞名稱。但是因為Tiger是一種Animal,它實現了AnimalContract,我們可以將它傳遞給getAnimalInfo並且它會接受它。我們建立Tiger in Go的方式略有不同,但用例是相同的。
Go
// Rest of main.go...
var tiger Tiger
tiger = Tiger{
Animal{
Name: "Tiger",
Height: 200,
Weight: 300,
},
}
tiger.Roar()
GetAnimalInfo(tiger)
正如您所看到的,當我們定義 Tiger
時,我們定義 Animal
,它是我們的嵌入式結構,就像它是一個屬性一樣,但我們不會將它與一個相關聯,不像我們對 Animal
上的 Name, Height and Weight
那樣的型別。
綜述
閱讀本文後,您應該能夠比較和理解PHP到Go中這些概念的差異,以及它們所具有的相似之處。