相信做个PHP的同学,在很多时候都使用过如下的方式去调用一个类中的方法,或者某个属性。
代码语言:php复制$method = "show";
$object = new A();
$object->$method();
在PHP中使用这种写法是完全没有问题的,但是做Go这种静态语言中,在编译阶段就会发生错误,因此在Go语言中不能使用该方式调用,而需要反射
机制来实现。
在实际的项目开发中,很多时候我们要实现某种功能,可能需要对接不同的平台,每个平台的接口肯定是不同的。但为了方便系统的维护、扩展。都会把不同平台的实现方式封装成一个扩展,然后在调用时通过一个工厂类去处理调用具体的扩展,只要保证每一个扩展中的返回参数格式一致就可以了。至于每一个扩展具体是怎么实现的,调用方根本不用关心,只需要关心入参和出参即可
。
为了保持每一个扩展中的返回参数方法,格式都保持一致,后期易于扩展。一般我们会封装一个接口,几口定义好提供给外部的方法,方法的接收参数和返回参数。如下示例代码:
代码语言:php复制interface A
{
public function show(): string;
public function run(): array;
}
class B implements A
{
public function show(): string
{
return __CLASS__ . __METHOD__ . __LINE__;
}
public function run(): array
{
return [
"class" => __CLASS__,
"method" => __METHOD__,
"line" => __LINE__,
];
}
}
class C implements A
{
public function show(): string
{
return __CLASS__ . __METHOD__ . __LINE__;
}
public function run(): array
{
return [
"class" => __CLASS__,
"method" => __METHOD__,
"line" => __LINE__,
];
}
}
class D
{
public function print(A $a, string $method)
{
print_r($a->$method());
}
}
$d = new D;
// 动态设置调用的类以及类下的方法
$d->print(new B, "run");
看完上面的代码,在回过头去考虑在Go语言是否可以这样实现呢?
在Go语言中,要实现这样的操作,可以采用这样的思路,但是在调用的地方就不能这么写。因为Go语言属于编译型语言,发现找不到对应的方法,就会编译不通过。
因此,Go语言提供了一种机制在运行时更新变量和检查他们的值,调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。
使用上面PHP的代码,我们用Go语言进行实现一次。
代码语言:go复制package main
import (
"fmt"
"reflect"
)
// 使用interface限定参数类型,动态调用struct中的方法、方法
type A1 interface {
Show1(name string)
}
type B1 struct {
Name1 string
Age1 uint
Sex1 string
IsOver1 bool
}
func (b1 B1) Show1(name string) {
fmt.Println(reflect.TypeOf(b1), name)
}
func (b1 B1) Run1() {
fmt.Print("b1->Run1()")
}
type C1 struct {
}
func (c1 C1) Show1(name string) {
fmt.Println(reflect.TypeOf(c1))
}
type D1 struct {
}
func (d1 D1) Run1(name string) {
fmt.Println(reflect.TypeOf(d1))
}
func printShow(a A1, menthod string, args ...interface{}) {
inputs := make([]reflect.Value, len(args))
for i, _ := range args {
inputs[i] = reflect.ValueOf(args[i])
}
// 动态调用struct中的方法
reflect.ValueOf(a).MethodByName(menthod).Call(inputs)
// 动态调用struct中的属性
fmt.Println("所有属性值", reflect.ValueOf(a).Elem())
fmt.Println("指定属性值", reflect.ValueOf(a).Elem().FieldByName("IsOver1"))
}
func main() {
// 使用此方式直接调用,
printShow(&B1{}, "Show1")
}