Go语言interface初印象——从豌豆射手到樱桃炸弹
Golang并没有类的概念,想要实现C++中的多态必须依赖interface,也就是接口
让我们构建一个例子,写植物大战僵尸中的豌豆射手、寒冰射手和樱桃炸弹
首先是C++
我们已知寒冰射手是豌豆射手的变种,只是子弹变成了冰块;而樱桃炸弹和两种射手又都是植物
点击查看C++代码
#include <iostream>
#include <string>
using namespace std;
class Plant {
public:
virtual ~Plant() = default;
virtual void attack() const = 0; // 纯虚函数
};
// 豌豆射手
class PeaShooter : public Plant {
protected:
string bullet = "豌豆";
public:
void attack() const override {
cout << "发射一颗" << bullet << "!\n";
}
};
// 寒冰射手(继承自豌豆射手,修改子弹类型)
class IceShooter : public PeaShooter {
public:
IceShooter() {
bullet = "冰冻豌豆"; // 在构造函数中修改子弹类型
}
};
// 樱桃炸弹(直接继承植物)
class CherryBomb : public Plant {
public:
void attack() const override {
cout << "Boom!爆炸!\n";
}
};
// 多态演示
void plantAttack(const Plant& plant) {
plant.attack();
}
int main() {
PeaShooter pea;
IceShooter ice;
CherryBomb bomb;
plantAttack(pea); // 输出:发射一颗豌豆!
plantAttack(ice); // 输出:发射一颗冰冻豌豆!
plantAttack(bomb); // 输出:Boom!爆炸!
return 0;
}
类的继承和多态其核心思想我认为是“派生”
“一生二,二生三,三生套娃”
那Golang呢?
在Go语言中还存在着另外一种类型:接口类型。接口类型是一种抽象的类型。它不会暴露出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会表现出它们自己的方法。也就是说当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的方法来做什么。
来自《Go语言圣经》
先来段代码
点击查看Golang代码
package main
import "fmt"
// Plant 接口,所有植物必须实现 Attack()和Type() 方法
type Plant interface {
Attack() string
Type() string
}
type BasePlant struct {
name string
}
// 默认实现 Type()
func (p BasePlant) Type() string {
return "plant"
}
// 豌豆射手
type PeaShooter struct {
BasePlant // 嵌入 BasePlant(继承 Type() 方法)
Bullet string // 子弹类型
}
func (p PeaShooter) Attack() string {
return fmt.Sprintf("发射一颗%s!", p.Bullet)
}
// 寒冰射手
type IceShooter struct {
PeaShooter // 嵌入 PeaShooter
}
// 樱桃炸弹
type CherryBomb struct {
BasePlant // 嵌入 BasePlant(继承 Type() 方法)
}
func (c CherryBomb) Attack() string {
return "Boom!爆炸!"
}
func main() {
// 初始化植物
pea := PeaShooter{
BasePlant: BasePlant{name: "豌豆射手"},
Bullet: "豌豆",
}
ice := IceShooter{
PeaShooter: PeaShooter{
BasePlant: BasePlant{name: "寒冰射手"},
Bullet: "冰冻豌豆",
},
}
bomb := CherryBomb{
BasePlant: BasePlant{name: "樱桃炸弹"},
}
// 统一调用 Plant 接口方法
plants := []Plant{pea, ice, bomb}
for _, plant := range plants {
fmt.Printf("[%s] %s\n", plant.Type(), plant.Attack())
}
}
真的很难为接口写一个很好的比喻,我只能尽力按照我的理解阐述一下
就像毛笔、铅笔、钢笔
笔是什么?能写字的就是笔,只不过这些笔的笔头、墨源、材质不一样,在Go中我们就能把笔的interface写入write()这一方法
暴论
在毛笔,钢笔,铅笔的结构体中写入这一方法write(),但是write的过程可以不一样
这样他们就会被go语言自动标上了“笔”这一性质
从而能够实现C++中class 笔的操作
真的好绕好绕好绕啊