首页 > 其他分享 >go语言中使用接口,以及对接口的理解

go语言中使用接口,以及对接口的理解

时间:2022-09-20 14:58:36浏览次数:70  
标签:Widget uuid 对接口 widget 接口 Go go 构造函数

go语言中使用接口,以及对接口的理解

接口的简单介绍

在任一编程语言中,接口 - 方法或行为的集合,在功能和该功能的使用者之间构建了一层薄薄的抽象层。在使用接口时,并不需要了解底层函数是如何实现的,因为接口隔离了各个部分(划重点)。

跟不使用接口相比,使用接口的最大好处就是可以使代码变得简洁。例如,您可以创建多个组件,通过接口让它们以统一的方式交互,尽管这些组件的底层实现差异很大。这样就可以在编译甚至运行的时候动态替换这些组件。

用 Go 的 io.Reader 接口举个例子。io.Reader 接口的所有实现都有 Read(p []byte) (n int, err error) 函数。使用 io.Reader 接口的使用者不需要知道使用这个 Read 函数的时候那些字节从何而来。

具体到 Go 语言

在我使用 Go 语言的过程中,与我使用过的其他任何编程语言相比,我经常发现其他的、不那么明显的使用接口的原因。今天,我将介绍一个很普遍的,也是我遇到了很多次的使用接口的原因。

Go 语言没有构造函数

很多编程语言都有构造函数。构造函数是定义自定义类型(即 OO 语言中的类)时使用的一种建立对象的方法,它可以确保必须执行的任何初始化逻辑均已执行。

例如,假设所有 widgets 都必须有一个不变的,系统分配的标识符。在 Python 中,这很容易实现:

import uuid

class Widget:
    # 使用构造函数初始化
    def __init__(self):
        self.__uuid = uuid.uuid1()

    def get_uuid(self):
        return self.__uuid

w = Widget()
print(w.get_uuid())

从上面这个例子可以看到,没有执行初始化逻辑就无法实例化一个新的 Widget

但是 Go 语言没有此功能。 在 Go 语言中,可以直接实例化一个自定义类型。

定义一个 Widget 类型:

package widgets

type Widget struct {
    id string
}

func (w Widget) ID() string {
    return w.id
}

可以像这样实例化和使用一个 widget

package main

import (
    "fmt"
    "github.com/krancour/widgets"
)

func main() {
    w := widgets.Widget{}
    fmt.Println(w.ID())
}

如果运行此示例,那么(也许)意料之中的结果是,打印出的 ID 是空字符串,因为它从未被初始化,而空字符串是字符串的“零值”。 我们可以在 widgets 包中添加一个类似于构造函数的函数来处理初始化:

package widgets

import uuid "github.com/satori/go.uuid"

type Widget struct {
    id string
}

func NewWidget() Widget {
    return Widget{
        id: uuid.NewV4().String(),
    }
}

func (w Widget) ID() string {
    return w.id
}

然后我们简单地修改 main 来使用这个类似于构造函数的新函数:

package main

import (
    "fmt"
    "github.com/krancour/widgets"
)

func main() {
    w := widgets.NewWidget()
    fmt.Println(w.ID())
}

执行该程序,我们得到了想要的结果。

但是仍然存在一个严重问题!我们的 widgets 包没有强制用户在初始一个 widget 的时候使用我们的构造函数。

变量私有化

首先我们尝试把自定义类型的变量私有化,以此来强制用户使用我们规定的构造函数来初始化 widget。在 Go 语言中,类型名、函数名的首字母是否大写决定它们是否可被其他包访问。名称首字母大写的可被访问(也就是 public ),而名称首字母小写的不可被访问(也就是 private )。所以我们把类型 Widget 改为类型 widget

package widgets

import uuid "github.com/satori/go.uuid"

type widget struct {
    id string
}

func NewWidget() widget {
    return widget{
        id: uuid.NewV4().String(),
    }
}

func (w widget) ID() string {
    return w.id
}

我们的 main 代码保持不变,这次我们得到了一个 ID 。这比我们想要的要近了一步,但是我们在此过程中犯了一个不太明显的错误。类似于构造函数的 NewWidget 函数返回了一个私有的实例。尽管编译器对此不会报错,但这是一种不好的做法,下面是原因解释。

在 Go 语言中,包*是复用的基本单位。其他语言中的类*是复用的基本单位。如前所述,任何无法被外部访问的内容实质上都是“包私有”,是该包的内部实现细节,对于使用这个包的使用者来说不重要。因此,Go 的文档生成工具 godoc 不会为私有的函数、类型等生成文档。

当一个公开的构造函数返回一个私有的 widget 实例,实际上就陷入了一条死胡同。调用这个函数的人哪怕有这个实例,也绝对在文档里找不到任何关于这个实例类型的描述,也更不知道 ID() 这个函数。Go 社区非常重视文档,所以这样做是不会被接受的。

轮到接口上场了

回顾一下,到目前为止,我们写了一个类似于构造函数的函数来解决 Go 语言缺乏构造函数的问题,但是为了确保人们用该函数而不是直接实例化 Widget ,我们更改了该类型的可见性——将其重命名为 widget,即私有化了。虽然编译器不会报错,但是文档中不会出现对这个私有类型的描述。不过,我们距离想要的目标还近了一步。接下来就要使用接口来完成后续的了。

通过创建一个可被访问的widget 类型可以实现的接口,我们的构造函数可以返回一个公开的类型实例,并且会显示在 godoc 文档中。同时,这个接口的底层实现依然是私有的,使用者无法直接创建一个实例。

package widgets

import uuid "github.com/satori/go.uuid"

// Widget is a ...
// 注意这里的接口名称首字母大写,可以在godoc文档中看到该接口下的方法以及注释
type Widgeter interface {
    // ID 返回这个 widget 的唯一标识符
    ID() string
}

type widget struct {
    id string
}

// NewWidget() 返回一个新的 Widget 实例
func NewWidget() Widgeter {
    return widget{
        id: uuid.NewV4().String(),
    }
}

func (w widget) ID() string {
    return w.id
}

总结

Go 语言的这一特质——构造函数的缺失反而促进了接口的使用。

标签:Widget,uuid,对接口,widget,接口,Go,go,构造函数
From: https://www.cnblogs.com/lichengguo/p/16711006.html

相关文章

  • go ssl
    packagemainimport("github.com/gin-gonic/gin""log""net/http")//测试go语言证书访问//opensslgenrsa-outserver.key2048//opensslreq......
  • googlepay-谷歌支付-一次性消耗inapp
    1.参考:https://zhuanlan.zhihu.com/p/5066367752.官方文档:https://developer.android.google.cn/google/play/billing/integrate3.Pub/subapi:https://cloud.google.com/......
  • python-接口自动化测试-基础知识(一)
    Python接口自动化测试理论知识以及框架源码一、什么是接口测试、为什么要做接口测试1、什么是接口测试接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测......
  • 我报名参加了Goldstone Project Phase 1 Challenge——瓜分100,000奖池,这是我的……
    我报名了GoldstoneProjectPhase1Challenge——瓜分100,000奖池,这是我的第10篇文章,点击查看活动详情Windows环境下安装下载并安装1.访问地址:https://zookeeper.apa......
  • js同一接口返回不同的类型之Edge的兼容
    前提:js同一接口返回不同的类型,但是这个链接上面的内容的缺点是没有加Edge浏览器的兼容:今天我们就说说如何加兼容:1.没有兼容内容的巩固,Chrome浏览器中获取blob的接口返......
  • java蓝途之接口
    接口接口:关键字interface接口的特点:1:接口不能直接创建对象,但是可以创建数组2:接口通常是用来被类实现的,使用关键字implements,实现之后,需要重写接口中的所有抽......
  • Go 中使用 Firebase 云消息传递的 Web 推送通知
    Go中使用Firebase云消息传递的Web推送通知关于网络推送通知,最近因为下雨不能出门而沉迷其中@_tetsuya28我将简要总结一下。这次,我将介绍一个从Go发送Web推送......
  • MongoDB 用户与权限
    1、创建查询role:custom_role,对dbidap_zl下的collection:tab1、tab2只有查询权限1)使用trs用户登录数据库2)切换到db:idap_zl创建role,替换示例中的collection,如果......
  • MAUI页面导航-await Shell.Current.GoToAsync();
    示例:Shell.Current.GoToAsync("..");//导航到前一页Shell.Current.GoToAsync(nameof(NotePage));//导航到Note页Shell.Current.GoToAsync($"{nameof(NotePage)}?{......
  • C++ 头文件接口设计浅谈
    C++头文件接口设计浅谈作者:独钓寒江雪链接:https://zhuanlan.zhihu.com/p/338227526对于很多出入门C++的程序员来说,大部门新手都是在用别人封装好的库函数,却没有尝试过......