首页 > 其他分享 >Go - Separate external calls from our main logic

Go - Separate external calls from our main logic

时间:2023-09-21 11:26:25浏览次数:55  
标签:return calls err Separate nil SingleItem field json external

Original implementation:

type SingleItem struct {
    Field    string  `json:"field"`
    Hour     int     `json:"hour"`
    Minute   int     `json:"minute"`
    ItemCode string  `json:"item_code"`
    Price    float64 `json:"price"`
    Quantity int     `json:"qty"`
}

type RawItems struct {
    Items            []SingleItem `json:"items"`
    TotalRecordCount int          `json:"total_record_count"`
    Start            int          `json:"start"`
}

type Calculator struct{}

func (c *Calculator) SomeComplexAggregationFunction(startDate, endDate time.Time, field string) (float64, error) {
    convertedStartTime := startDate.Format("2006-02-01")
    convertedEndTime := startDate.Format("2006-02-01")
    rawResp, err := http.Get(fmt.Sprintf("http://example-data-server/api/data-archive/v1/retail?field=%v&start-date=%v&end-date=%v", field, convertedStartTime, convertedEndTime))
    if err != nil {
        return 0.0, err
    }
    if rawResp.StatusCode != http.StatusOK {
        return 0.0, fmt.Errorf("unexpected status code")
    }
    raw, err := io.ReadAll(rawResp.Body)
    if err != nil {
        return 0.0, err
    }
    var items RawItems
    err = json.Unmarshal(raw, &items)
    if err != nil {
        return 0, err
    }
    // Pretend this is some complex calculation
    summer := 0.0
    for k, v := range items.Items {
        fmt.Printf("processing current item: %v", k)
        summer = float64(v.Quantity)*v.Price + summer
    }
    return summer, nil
}

 

Refactored implementation:

type V1InternalEndpoint struct{}

func (e *V1InternalEndpoint) Retrieve(startDate, endDate time.Time, field string) ([]SingleItem, error) {
    convertedStartTime := startDate.Format("2006-02-01")
    convertedEndTime := startDate.Format("2006-02-01")
    rawResp, err := http.Get(fmt.Sprintf("http://example-data-server/api/data-archive/v1/retail?field=%v&start-date=%v&end-date=%v", field, convertedStartTime, convertedEndTime))
    if err != nil {
        return []SingleItem{}, err
    }
    if rawResp.StatusCode != http.StatusOK {
        return []SingleItem{}, fmt.Errorf("unexpected status code")
    }
    raw, err := io.ReadAll(rawResp.Body)
    if err != nil {
        return []SingleItem{}, err
    }
    var items RawItems
    err = json.Unmarshal(raw, &items)
    if err != nil {
        return []SingleItem{}, err
    }
    return items.Items, nil
}

type DataRetriever interface {
    Retrieve(startDate, endDate time.Time, field string) ([]SingleItem, error)
}

type Calculator struct {
    d DataRetriever
}

func (c *Calculator) SomeComplexAggregationFunction(startDate, endDate time.Time, field string) (float64, error) {
    items, err := c.d.Retrieve(startDate, endDate, field)
    if err != nil {
        return 0, err
    }
    // Pretend this is some complex calculation
    summer := 0.0
    for k, v := range items {
        fmt.Printf("processing current item: %v", k)
        summer = float64(v.Quantity)*v.Price + summer
    }
    return summer, nil
}

Notice how much simpler the function becomes as we move the external call out of the function where we implement the logic which would contain our main logic. The call to retrieve data could be a real piece of code that actually does external calls and retrieve data via JSON or thrift, or GRPC protocols. It could also be a piece of code that provides fake data that we could test our application against.

 

Seeing that our code now relies on DataRetriever interface, we would need to build our mock against it and ensure that it follows that function signature.

type FakeDataRetriever struct{}

func (f *FakeDataRetriever) Retrieve(startDate, endDate time.Time, field string) ([]SingleItem, error) {
    if field == "receipts" {
        return []SingleItem{SingleItem{Price: 1.1, Quantity: 2}}, nil
    }
    return []SingleItem{}, fmt.Errorf("no data available")
}

We can specify what kind of data that might be returned from the external calls:
• Maybe an array of 1,000 items could be returned
• Maybe include invalid data 

You can probably extend this concept further, such as database calls; application calls to queue systems such as Kafka and Nats; or application calls to caches such as Redis, and so on. All of this can be mocked and have unit tests be run against the
logic that we write up—it is just that it takes a bit of effort to maintain such mocking code.

 

标签:return,calls,err,Separate,nil,SingleItem,field,json,external
From: https://www.cnblogs.com/zhangzhihui/p/17719447.html

相关文章

  • 【错误记录】Android Studio 创建 Module 模块报错 ( Cannot resolve external depend
    文章目录一、报错信息二、解决方案目前使用的是最新的Gradle配置,创建Module生成的源码与Gradle配置出现了冲突,导致的问题;解决此类问题,要仔细检查Gradle构建脚本,排查每个依赖库的来源;本次错误就是AS系统自动成的Module修改了Gradle构建脚本,导......
  • Apache IoTDB开发之Load External TsFile工具
    LoadExternalTsFile工具简介加载外部tsfile工具允许用户从正在运行的ApacheIoTDB实例加载tsfiles、删除tsfile或将tsfile移动到目标目录。用法用户通过Cli工具或JDBC向ApacheIoTDB系统发送指定的命令以使用该工具。加载TS文件加载tsfiles的命令是。load"<p......
  • 使用svn.externals(外链)提升美术多个svn目录的svn up速度
    svnup多个目录耗时大svn上的美术资源项目,在打包机上对一个很久没有变化的目录进行svnup也是需要消耗不少时间的,特别打包时需要对多个目录进行svnup,比如空跑54个目录的svnup耗时在3分多钟,而通过svn:externals把这些目录外链到一个目录下,可以大大减少svnup的时间。关于svn:e......
  • webpack生产环境优化:externals
    转载请注明来源:http://www.eword.name/Author:ewordEmail:[email protected]生产环境优化:externalsexternals配置主要是为了防止某些通过CDN引入的包被打包到输出的bundle中。一、核心配置/*webpack.config.jswebpack的配置文件路径:./webpack.config......
  • webpack生产环境优化:externals
    转载请注明来源:http://www.eword.name/Author:ewordEmail:[email protected]生产环境优化:externalsexternals配置主要是为了防止某些通过CDN引入的包被打包到输出的bundle中。一、核心配置/*webpack.config.jswebpack的配置文件路径:./webpack.config......
  • IDEA 项目里的 External Libraries 突然变得干干净净
     怎么会这样子咧!昨天还好好的!很气~! 1、先检查一下 Maven 2、然后把项目下面的.idea删了  3、最后把项目关了再重新打开就好了。  解决方案:......
  • Kubernetes Service中的 external-traffic-policy 是什么?
    【摘要】external-traffic-policy,顾名思义“外部流量策略”,那这个配置有什么作用呢?以及external是指什么东西的外部呢,集群、节点、Pod?今天我们就来学习一下这个概念吧。1、什么是external-traffic-policy在k8s的Service对象(申明一条访问通道)中,有一个“externalTrafficPolicy”......
  • k8s Service(一) ClusterIP、NodePort、LoadBalancer、externalIPs 演示
    目录:Service简介kube-proxy3种不同的数据调度模式Service资源的定义格式示例1:ClusterIP演示示例2:NodePort演示示例3:LoadBalancer演示示例4:externalIPs演示Service简介Service:可以理解为pod的负债均衡器,标准资源类型,ServiceController为动态的一组Pod提......
  • How to get User Name from External Login in ASP.NET Core?
    HowtogetUserNamefromExternalLogininASP.NETCore? 回答1DoyouhaveaccesstoSignInManagerorcanyouinjectit?Ifyes,thenthisishowyouwouldaccessuserid(username),email,first&lastname:publicclassMyController:Microsoft.Asp......
  • 前端项目实战拾肆-window上‘PORT‘ is not recognized as an internal or external c
    mac"scripts":{"dev":"PORT=3005nextdev","build":"nextbuild","start":"nextstart","lint":"nextlint","test":"jest"......