当前位置: 首页 > 面试题库 >

转到界面字段

岳景明
2023-03-14
问题内容

我熟悉一个事实,在Go中,接口定义功能而不是数据。您在接口中放置了一组方法,但是您无法指定实现该接口的任何内容所必需的任何字段。

例如:

// Interface
type Giver interface {
    Give() int64
}

// One implementation
type FiveGiver struct {}

func (fg *FiveGiver) Give() int64 {
    return 5
}

// Another implementation
type VarGiver struct {
    number int64
}

func (vg *VarGiver) Give() int64 {
    return vg.number
}

现在我们可以使用该接口及其实现:

// A function that uses the interface
func GetSomething(aGiver Giver) {
    fmt.Println("The Giver gives: ", aGiver.Give())
}

// Bring it all together
func main() {
    fg := &FiveGiver{}
    vg := &VarGiver{3}
    GetSomething(fg)
    GetSomething(vg)
}

/*
Resulting output:
5
3
*/

现在,您 不能 做的是这样的事情:

type Person interface {
    Name string
    Age int64
}

type Bob struct implements Person { // Not Go syntax!
    ...
}

func PrintName(aPerson Person) {
    fmt.Println("Person's name is: ", aPerson.Name)
}

func main() {
    b := &Bob{"Bob", 23}
    PrintName(b)
}

但是,在研究了接口和嵌入式结构之后,我发现了一种遵循以下方式的方法:

type PersonProvider interface {
    GetPerson() *Person
}

type Person struct {
    Name string
    Age  int64
}

func (p *Person) GetPerson() *Person {
    return p
}

type Bob struct {
    FavoriteNumber int64
    Person
}

由于具有嵌入式结构,Bob拥有Person所拥有的一切。它还实现了PersonProvider接口,因此我们可以将Bob传递到旨在使用该接口的函数中。

func DoBirthday(pp PersonProvider) {
    pers := pp.GetPerson()
    pers.Age += 1
}

func SayHi(pp PersonProvider) {
    fmt.Printf("Hello, %v!\r", pp.GetPerson().Name)
}

func main() {
    b := &Bob{
        5,
        Person{"Bob", 23},
    }
    DoBirthday(b)
    SayHi(b)
    fmt.Printf("You're %v years old now!", b.Age)
}

这是一个演示上面代码的Go
Playground

使用这种方法,我可以创建一个定义数据而不是行为的接口,并且可以通过嵌入任何数据而通过任何结构实现该接口。您可以定义与嵌入式数据显式交互且不知道外部结构的性质的函数。并在编译时检查所有内容!(你可以搞砸了,我能看到的唯一方法,将嵌入界面PersonProviderBob,而不是一个具体的Person,它会编译并在运行时失败。)

现在,这是我的问题:这是一个巧妙的窍门,还是我应该以不同的方式来做?


问题答案:

这绝对是一个巧妙的把戏。但是,公开指针仍然可以直接访问可用数据,因此,它只为您提供了额外的灵活性,以便将来进行更改。此外,
Go约定不要求您始终将抽象放在数据属性前面

综合考虑这些因素,对于给定的用例,我倾向于一个极端或另一极端:要么a)仅仅设置一个公共属性(在适用时使用嵌入),然后传递具体类型,要么b)如果看起来像暴露数据那​​样稍后造成麻烦,请公开获取器/设置器以获得更可靠的抽象。

您将在每个属性的基础上进行权衡。例如,如果某些数据是特定于实现的,或者由于其他原因您希望更改表示形式,则您可能不想直接公开该属性,而其他数据属性可能足够稳定,因此将其公开是绝对的胜利。

将属性隐藏在getter和setter的后面,可以为以后提供向后兼容的更改提供更多的灵活性。假设您某天想更改Person为不仅存储单个“名称”字段,还存储第一/中间/最后/前缀;如果您有方法Name()stringSetName(string),则可以Person在添加新的更细粒度的方法的同时让界面的现有用户满意。或者,您可能希望在未保存更改的情况下将数据库支持的对象标记为“脏”。您可以在数据更新全部通过SetFoo()方法时执行此操作。

因此:使用getters / setter方法,您可以在维护兼容API的同时更改struct字段,并在属性get /sets周围添加逻辑,因为没有人p.Name = "bob"无需浏览代码就可以这样做。

当类型复杂(并且代码库很大)时,这种灵活性更为重要。如果您有一个PersonCollection,则它可能在内部由sql.Rows,一个[]*Person,一个[]uint数据库ID或其他ID支持。使用正确的界面,您可以节省呼叫者的烦恼,io.Reader使网络连接和文件看起来像样。

一件事:interfaceGo中的s具有独特的属性,您无需导入定义它的包就可以实现它。这可以帮助您避免周期性进口。如果您的接口返回一个*Person,而不只是字符串或其他任何东西,则都PersonProviders必须将包导入Person定义的位置。这可能是好的,甚至是不可避免的;这只是要知道的结果。

但是同样, Go社区没有严格的约定禁止在类型的公共API中公开数据成员
。在给定情况下,将对属性的公共访问作为API的一部分使用是否合理是您的判断,而不是阻止 任何 公开,因为这可能会使以后的复杂化或阻止实现更改,是否合理。

因此,例如,stdlib做类似的事情,让您http.Server使用配置初始化a
并承诺可以使用零bytes.Buffer。这样做自己的事很好,而且,实际上,如果更具体的,数据公开的版本似乎可行,那么我不建议您先将东西抽象掉。这只是要注意权衡。



 类似资料:
  • 问题内容: 我怎么知道我可以从对象/界面访问的字段?我尝试过反射,但看来您必须首先知道字段名称。如果我需要了解所有可用字段怎么办? 问题答案: 您可以使用该函数获取类型描述符。从那里,您可以列出存储在界面中的动态值的字段。 例: 输出: 调用的结果是一个值为,其中包含字段名称以及其他内容: 如果还需要这些字段的值,则可以使用获取,然后可以使用或: 输出: 在Go Playground上尝试一下。

  • 第一次运行 Burp 时,您需要花一点时间检查您的显示设置。 Burp 可以让您定制 UI,为不同部分选择不同大小的字体,您可能需要根据屏幕分辨率更改这些设置。 首先,查看 Burp 菜单,标签标题,按钮和其他文本中显示的文本。 如果要更改主 UI 字体大小,请转到选项(User Options)选项卡,然后转到显示(Display)子选项卡,在用户界面部分中选择字体大小。 然后重新启动 Burp

  • 我们将 Sketch 的界面设计的非常简洁。最顶端的工具箱包含了最重要的操作。你可以用右侧的检查器来调整被选中图层的内容,左侧的窗口则会列出文件中的所有图层,中间当然就是你正在创作的画布。 Sketch 里没有浮动面板,检查器将会根据你选中的工具来显示所需控件,这样你能始终不受打扰的在画布上创作。 画布 Sketch 的画布尺寸是无限的,可以向任意方向无限延伸,你将拥有绝对的自由来规划自己的创作区

  • 用户界面 首先,我们需要熟悉mitmproxy的用户界面。打开您在其中启动mitmproxy的终端窗口。您处于mitmproxy的默认视图中,该视图显示流列表。您应该看到浏览器的HTTP请求以加载本教程。随着新请求的出现,mitmproxy将行添加到视图中。 命令效果请查看 用户界面 影片内容 欢迎来到mitmproxy教程。在本课程中,我们将介绍用户界面。 这是mitmproxy的默认视图。 随

  • 交互 showToast 基础库1.3.9开始支持,iOS版本2.1.23,Android版本2.1.38 showToast(Object object) 显示消息提示框。 参数 Object object 属性 类型 默认值 必填 说明 title string 是 提示的内容 icon string 'success' 否 图标 image string 否 自定义图标的本地路径,image

  • 一、简介 1.用于管理站点的前台模版页面 2.标签向导主要用于自动化生成数据调用标签。 二、功能演示 模版风格 标签向导