Go知识点-为什么在Go中要使用接口

Go知识点-为什么在Go中要使用接口


如果你已经写过一段时间的代码了,那我可能就不需要解释接口的优点了,相信你也应该深有感触。但是在深入研究为什么我在Go中使用接口之前,我需要花一点时间来介绍一下

如果你对接口足够了解,那你可以跳过这段

Level setting


通过接口,我们可以整合一组方法或者行为,这个在任何编程语言里面都是想通的。这样的话,相当于在功能的定义和消费者之间设置了一层抽象。书写接口的一方,不需要实现接口方法的业务细节。这样保证了各个组件功能的清晰。

你可以通过接口实现很多别的方式无法实现的酷炫功能。例如,你创建了很多组件,他们通过某段代码以相同的方式进行交互,组件再大也无所谓。这样的话,在我们切换组件的时候,就可以无缝衔接,甚至可以在运行时动态的交换。

在Go中的 io.Reader 就是一个真实的例子,所有实现io.Reader的接口,都需要提供一个Read(p []byte) (n int, err error) 的方法, 实现io.Reader 接口的调用方不需要知道 byte数组从什么地方传过来的。

以上所说的所有内容,对于从事过一段时间开发工作的人来说,这都是常识。

Go中的接口


在Go中,使用情况比其他语言多很多,这里我要介绍一些编码中,跟接口有关的常见问题。

Go没有构造器方法


很多语言都会提供一个构造器方法,构造器允许用户自定义一些类型的实例化规则(在很多面向对象的语言中,成为类),保证一些操作在初始化的过程中完成。
例如,很多对象都会有一个不可变得,系统分配的唯一标识符。这个在Java中很容易实现:

package io.krancour.widget;

import java.util.UUID;

public class Widget {

    private String id;

    // A constructor that performs some initialization!
    public Widget() {
        id = UUID.randomUUID().toString();
    }

    public String getId() {
        return id;
    }

}
 
 class App {

        public static void main( String[] args ){
            Widget w = new Widget();
            System.out.println(w.getId());
        }

}

在这里,你不可能跳过他的实例化方法,创建一个新的Widget 对象,但是Go却没有这个功能

在Go中,可以直接实例化用户自定义对象
给一个例子:

package widgets

type Widget struct {
    id string
}

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

package main

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

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

如果你执行上面的代码,就会发现得到的结果跟我们预期的不一样,打印出来的是一个空字符串。因为我们并没有实例化ID的值,所以ID是的值是string的"零值"。

我们可以添加一个方法,让他达到我们预期的效果:

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
}

package main

import (
    "fmt"

    "github.com/krancour/widgets"
)

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

执行上面的代码,我们得到了想要的结果。

但是仍然有一个巨大的问题,我们没有办法组织用户通过默认的方式实例化Widget结构体

Go 的私有化


当我们通过"类似构造器"方法初始化我们的Widgets结构体的时候,首先要确保他是私有的。在Go中,一个类型或者函数等的结构大小在开始的时候就被确定了(对其他包可见的), 首字母大写的是共有的,首字母小写的是私有的。因此我们要把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函数不需要修改,程序也能正常执行。这个结果跟我们预期的结果越来越接近了,但是NewWidget()方法返回的是一个私有的结构体类型。虽然编译器没有报错,但是这始终是一个不太好的方式,需要一些额外的解释。

在Go语言中。package是一个基本单位(其他语言一般把类作为一个单位)。就像前面说的一样,任何私有结构体(首字母小写)的内容都应该是私有的。私有的内容应该包括很多实现的细节,而且不应该对外部调用的对象产生任何影响。(在这里如果私有结构体改变了属性,那调用方就不对了)而且,在这里,godoc指令也不会为私有的类型生成文档。

我们构造了一个类似构造器的,返回私有类型实例的方法。无意中为文档内容创建了一个"死胡同"。 调用这个方法虽然可以获取实例,但是无法调用里面的ID()方法,而且也不能获取到更多关于widget的相关细节。Go社区内非常重视文档,因此这个方式可能不行。

使用Interface来救场

直到现在,我们一直在通过构建一个类似构造函数的方式来获取实例化对象,为了保证用户始终调用这个方法构建对象,我们用了私有化结构体类型。尽管编译器允许这样做,但是文档的问题却无法解决。接下来我们会通过接口的方式来完善它。

首先创建一个的公有的接口类型,然后widget来实现这个接口。完美的实现了我们之前的所有要求。

package widgets

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

// Widget is a ...
type Widget interface {
    // ID returns a widget's unique identifier
    ID() string
}

type widget struct {
    id string
}

// NewWidget() returns a new Widget
func NewWidget() Widget {
    return widget{
        id: uuid.NewV4().String(),
    }
}

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

包装


Go语言缺乏构造函数,因此我们需要通过接口的方式来帮助完成这件事,否则他们可能无法被调用,希望我的介绍,已经充分包括了这部分的所有细节。

我的下一篇文章中,将会介绍一个 关于接口的,在其他语言很常见的使用情景,但是在Go中却无法做到的事情。

原文链接:Go Pointers: Why I Use Interfaces (in Go)
作者:Kent Rancourt
译者:JYSDeveloper

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,270评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,489评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,630评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,906评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,928评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,718评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,442评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,345评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,802评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,984评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,117评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,810评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,462评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,011评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,139评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,377评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,060评论 2 355

推荐阅读更多精彩内容