Go for PHP Developers: Structs vs Classes (翻译)

Go for PHP Developers: Structs vsClasses (翻译)

原文链接: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中这些概念的差异,以及它们所具有的相似之处。

原文链接:Go for PHP Developers: Structs vsClasses

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信搜索:上帝喜爱笨人
本帖由 zhaocrazy 于 2年前 加精
讨论数量: 2
 map[string]interface{}{
        "name": a.Name,
        "height": a.Height,
        "weight": a.Weight,
    }

interface可以表示接口和任何类型吗?

4年前 评论
JaguarJack

@lovecn 是的 interface 可以接受任何类型

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!