本文介绍: Golanginterface接口特性能够实现提供&使用之间关系的解耦,使得代码行文更加规范立体。本文就来通过一个简单缓存cache模块实现,来示范一下Golanginterface该怎么用。

和许多面向对象编程语言一样,Golang存在interface接口这样的概念interface相当于是一个中间层,下游只需要关心interface实现什么行为利用这些行为做些业务级别事情,而上游则负责实现interface,把这些行为具象化。本文就来通过一个简单的缓存cache模块的实现,来示范一下Golanginterface该怎么用。

首先,从业务service角度而言,一个cache模块可能需要以下几种方法

那么这些个方法,就可以用一类叫Cacheinterface表示

type Cache interface {
    Get(key string) (interface{}, bool)
    Set(key string, value interface{})
    SetExpire(key string, value interface{}, expire time.Duration)
    Delete(key string)
}

其中,Get方法返回一个interface{}value,以及是否存在bool标识SetSetExpire表示无时限跟有时限的缓存行为Delete表示删除缓存内容。整块Cache接口定义也非常明显。

这样写有什么好处?如果你是下游业务服务的话,你只需要这样写就可以了。这里给一个同package下的测试用例代码

func TestCache(t *testing.T) {
    k, v := "hello", "world"
    // Current()的实现,在下文慢慢解释
    var curCache Cache = Current()

    // set & get & delete
    curCache.Set(k, v)
    cached, ok := curCache.Get(k)
    if !ok {
        t.Fatalf("cannot cache %s:%s", k, v)
    } else {
        t.Logf("got cached %s:%v (type: %s)", k, cached, reflect.TypeOf(cached).Name())
    }
    curCache.Delete(k)
    _, ok = curCache.Get(k)
    if ok {
        t.Fatalf("cannot delete %s:%s", k, v)
    } else {
        t.Logf("delete cached %s:%s", k, v)
    }

    // set expire
    curCache.SetExpire(k, v, 1*time.Second)
    cached, ok = curCache.Get(k)
    if !ok {
        t.Fatalf("cannot cache %s:%s", k, v)
    } else {
        t.Logf("got cached %s:%v (type: %s)", k, cached, reflect.TypeOf(cached).Name())
    }
    time.Sleep(3 * time.Second)
    _, ok = curCache.Get(k)
    if ok {
        t.Fatalf("cannot expire %s:%s", k, v)
    } else {
        t.Logf("expired %s:%s", k, v)
    }
}

可以看到我们指定缓存对象curCache标识为一个Cache,是个接口定义,这样标识起来的话,下面的代码就可以正常使用GetSet之类的方法了。而更重要的是,下面的代码,不会因为Cache的具体实现变化而有所变化。举个例子,你有10个开源缓存库,想定时切换Current() Cache背后的缓存对象实现,就算你再怎么换,只要用到缓存的代码标注缓存对象Cache这个interface,并且interface定义没有变化,那么使用缓存的代码就不需要动。这样,就彻底实现了缓存提供方和使用方的解耦,开发效率也会噌噌噌的上去。

既然提到了提供方Provider概念,那在缓存的实现上,就可以走依赖注入控制反转模式假设某个Web服务有个本地缓存模块,在实现上,就可以考虑提供多个Cache接口的实现,同时在配置指定默认的一种。这里,就以go-cache为例,做一个实现案例

import (
    "github.com/patrickmn/go-cache"
    "time"
)

const (
    GoCacheDefaultExpiration = 10 * time.Minute
    GoCacheCleanupInterval   = 15 * time.Minute
)

type GoCache struct {
    c *cache.Cache

    defaultExpiration time.Duration
    cleanupInterval   time.Duration
}

func (g *GoCache) Get(key string) (interface{}, bool) {
    return g.c.Get(key)
}

func (g *GoCache) Set(key string, value interface{}) {
    g.c.Set(key, value, GoCacheDefaultExpiration)
}

func (g *GoCache) SetExpire(key string, value interface{}, expire time.Duration) {
    if expire < 0 {
        expire = g.defaultExpiration
    }
    if expire > g.cleanupInterval {
        expire = g.cleanupInterval
    }
    g.c.Set(key, value, expire)
}

func (g *GoCache) Delete(key string) {
    g.c.Delete(key)
}

func NewGoCache() *GoCache {
    return &amp;GoCache{
        c: cache.New(GoCacheDefaultExpiration, GoCacheCleanupInterval),

        defaultExpiration: GoCacheDefaultExpiration,
        cleanupInterval:   GoCacheCleanupInterval,
    }
}

我们定义一个GoCachestruct,实现了Cache接口定义所有行为,那么GoCache实例,在Golang里,就能够标识为一个Cache接口实例NewGoCache方法,不仅是提供了一个GoCache的实例,而在业务层面,更是提供了一个Cache实例。因此,我们可以简单用一个map管理所有Cache构造器,从而标识不同的缓存实现:

func provideGoCache() Cache {
    return NewGoCache()
}

var cacheProviders = map[string]Cache{
    "go-cache": provideGoCache(),
}

const (
    DefaultCacheProvider = "go-cache"
)

func Get(provider string) Cache {
    c, ok := cacheProviders[provider]
    if !ok {
        return nil
    }
    return c
}

func Default() Cache {
    return Get(DefaultCacheProvider)
}

// 上文提到的样例代码,就用了这个方法拿到go-cache实现的Cache接口实例
func Current() Cache {
    return Default()
}

显而易见,通过这样的一个代码组织,不论是go-cache,抑或是其它的Cache实现,都可以集中管理并灵活取用。这,便是interfaceGolang编程中给我们带来的便利了。

原文地址:https://blog.csdn.net/u013842501/article/details/129100993

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_47478.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注