方法的Local-type


今天学习casbin的时候,因为受老板的代码形式的影响,想把最基本的代码封装开来,无意间对方法的使用有了新的理解

本来我以为方法就是函数,随便一个函数,只要在函数名前加个方法类型即可,后来才发现我忽略了一个细节

GPT回答:

​ 在Go语言中,方法的接收者类型必须是在同一个包内定义的命名类型(named type)。这意味着你无法在其他包中定义的类型上直接添加方法。如果你需要为非本地类型添加方法,你可以通过创建一个新的类型来包装它,然后为新类型添加方法。

  • 版本一(最简单)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func GenCasbin() {
e, err := casbin.NewEnforcer("./casbin/model.conf", "./casbin/policy.csv")
if err != nil {
fmt.Println("NewEnforce err:", err)
return
}

sub := "alice" // the user that wants to access a resource.
obj := "data1" // the resource that is going to be accessed.
act := "read" // the operation that the user performs on the resource.

enforce, err := e.Enforce(sub, obj, act) // true
if err != nil {
fmt.Println("Enforce err:", err)
return
}
if enforce {
fmt.Println("permit alice to read data1")
// permit alice to read data1
} else {
fmt.Println("deny the request, show an error")
// deny the request, show an error
}
}

这个版本就是最原始的按照官方文档写的,但是看着这么丑陋的代码,脑子里总想着给他拆开来。

所以有了后面的代码

  • 版本二(有错误)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func GenCasbin() {
e, err := casbin.NewEnforcer(“./casbin/model.conf”, “./casbin/policy.csv”)
if err != nil {
fmt.Println(“NewEnforce err:”, err)
return
}

sub := "alice" // the user that wants to access a resource.
obj := "data1" // the resource that is going to be accessed.
act := "read" // the operation that the user performs on the resource.

e.GetEnforcer(sub, obj, act)
}
// 上面的代码有错误,先别看,先看下面的代码
func (e *casbin.Enforcer) GetEnforcer (sub, obj, act string)error{
enforce, err := e.Enforce(sub, obj, act) // true
if err != nil {
return err
}
if enforce {
fmt.Println(“成功访问”)
} else {
fmt.Println(“无权限访问”)
}
return nil
}

我先把Enforcer的功能拆开来,然后想要使用方法 *func (e casbin.Enforcer) GetEnforcer (sub, obj, act string)error{}

但是我发现**(e *casbin.Enforcer)**这个代码一直都是呈现红色

查询GPT后发现,casbin.Enforcer是casbin包的类型

因此总是会返回错误:Invalid receiver type ‘*casbin.Enforcer’ (‘casbin.Enforcer’ is a non-local type)

正确的方法就是把他变成local-type

1
2
3
type Enforcer struct {
*casbin.Enforcer
}

说到这个local-type的实现形式,我突然对老板之前的dao包的操作有了新的理解

1
2
3
4
5
6
var (
Permission = &permission{}
)
type permission struct {
*gorm.DB
}

通过这个封装会发现,我们不仅可以使用方法,还可以表明这个数据库是Permission的数据库

更加的清晰,模块化

  • 版本三(有错误)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
type Enforcer struct {
*casbin.Enforcer
}

func GenCasbin() {
e, err := casbin.NewEnforcer("./casbin/model.conf", "./casbin/policy.csv")
if err != nil {
fmt.Println("NewEnforce err:", err)
return
}

sub := "alice" // the user that wants to access a resource.
obj := "data1" // the resource that is going to be accessed.
act := "read" // the operation that the user performs on the resource.

e.GetEnforcer(sub, obj, act) // 这里有问题
}
}

func (e Enforcer) GetEnforcer (sub, obj, act string)error{
enforce, err := e.Enforce(sub, obj, act) // true
if err != nil {
return err
}
if enforce {
fmt.Println("成功访问")
} else {
fmt.Println("无权限访问")
}
return nil
}

本来我以为,经过版本二的修改,代码就差不多了

但是我发现 e.GetEnforcer(sub, obj, act) 这句话的方法使用似乎又出现了问题(好吧,本来就有问题。。)

再次从 local-type的起点出发,发现*方法的使用者e的类型是 casbin.Enforcer

由此我们可以得出,方法的使用需要满足type-local

  1. 使用者类型的本地性
  2. 接收者类型的本地性

修改后变成版本四

  • 版本四(正确)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
type Enforcer struct {
*casbin.Enforcer
}

func NewEnforcer(modelPath, policyPath string) (*Enforcer, error) {
e, err := casbin.NewEnforcer(modelPath, policyPath)
if err != nil {
return nil, err
}
return &Enforcer{e}, nil
}

func (e Enforcer) GetEnforcer(sub, obj, act string) error {
enforce, err := e.Enforce(sub, obj, act) // true
if err != nil {
return err
}
if enforce {
fmt.Println("成功访问")
// permit alice to read data1
} else {
fmt.Println("无权限A访问")
// deny the request, show an error
}
return nil
}

func RunCasbin() {
e, err := NewEnforcer("./casbin/model.conf", "./casbin/policy.csv")
if err != nil {
fmt.Println(err)
}
sub := "alice" // the user that wants to access a resource.
obj := "data1" // the resource that is going to be accessed.
act := "read" // the operation that the user performs on the resource.

err1 := e.GetEnforcer(sub, obj, act)
if err1 != nil {
fmt.Println(err1)
}
}

最后输出: 无权限A访问

哈哈哈哈哈哈哈哈