go - 创建需要其他可重复逻辑作为先决条件的函数(干净的代码)

标签 go dependency-injection interface

我有以下 yaml 文件,我需要对其进行解析(解析按预期工作)并且需要从 yaml 文件内容中提供一个 data由以下解耦函数

公开

我需要提供以下功能(这里是其中一些功能的示例,需要更多具有相同模式的功能...)

getApps()

getServices()

GetApp(appname)

GetServiceForApp(appname)

这是代码(有效...)

var DMZ = []byte(`
applications:
  - name: app1
    type: php
    src: /app1
    host: us
    use: 
      - redis
      - mysql

  - name: app2
    type: rust
    src: /app2
    host: eu
    use: 
      - mongo
      - mysql

  - name: app3
    type: golang
    src: /app3
    host: us
    use: 
      - postgress
      - mysql


services:
  - name: mongo
    type: db
    host: us

  - name: mysql
    type: db
    host: eu

  - name: postgress
    type: db
    host: us

  - name: redis
    type: db
    host: us   
`)

这是结构

type DMZ struct {
  Applications       []*Applications   `yaml:"applications,omitempty"`
  Services           []*Services       `yaml:"services,omitempty"`
}

type Applications struct {
  Name        string
  Type        string
  Src        string            `yaml:"src,omitempty"`
  use        []Use             `yaml:"use,omitempty"`
}
type Services struct {
  Name        string
  Type        string
  Host        string            `yaml:"host,omitempty"`
}
type Use struct {
  Name       string     `yaml:"name,omitempty"`
  host       string     `yaml:"host,omitempty"`
  Type       string     `yaml:"type,omitempty"`
}



// Parse file
func Parse(yamlContent []byte) (out DMZ, err error) {
  dmz := DMZ{}
  err = yaml.Unmarshal([]byte(yamlContent), &dmz)
  if err != nil {
    logs.Error("Yaml file is not valid, Error: " + err.Error())
  }
  return dmz, err
}

因为 Parse 函数是一个per-requite 所有需要的函数(我在上面列出)我想知道 per-requite strong>best 创建它们,创建每次 调用parse 函数然后执行逻辑(不是问题)的简单函数,但我想知道是否是否有更好的方法,它遵循 Golang 的干净代码原则,带有“接口(interface)/依赖项注入(inject)”

更新:

我想避免做以下事情,假设我需要从不同的包甚至不同的GitHub存储库调用这些函数如何最好地使用 Golang 干净的代码。

func getApps(){

 dmz := Parse()
....
}


func getServices(){

 dmz := Parse()
....

}


func getApp(appname string){

 dmz := Parse()
....

}


func GetServiceForApp(appname string){

 dmz := Parse()
....

}

我需要更多具有相同模式的功能......

我需要一些使用接口(interface)/依赖项注入(inject)Clean Code解决方案,例如 Golang 中的最佳实践代码示例

如果有什么不清楚的地方请告诉我:)

最佳答案

解析指针类型结构值中的值,它将存储指针指向的结构中的值。然后 Create 方法接收所有你想从中获取结构内应用程序或服务值的方法。

在所有方法中使用指针接收器,您将能够访问在解析 yaml 时更新的原始结构。

创建一个指向该结构的空实例的指针并将其作为方法接收器传递。然后在解码中访问该结构以更新原始结构中的 yaml 数据。通过使用指向方法的指针接收器访问原始结构的值来访问每个函数中更新的结构。

package main

import (
    "fmt"
    "log"

    yaml "gopkg.in/yaml.v2"
)

var dmz = []byte(`
applications:
  - name: app1
    type: php
    src: /app1
    host: us
    use: 
      - redis
      - mysql

  - name: app2
    type: rust
    src: /app2
    host: eu
    use: 
      - mongo
      - mysql

  - name: app3
    type: golang
    src: /app3
    host: us
    use: 
      - postgress
      - mysql


services:
  - name: mongo
    type: db
    host: us

  - name: mysql
    type: db
    host: eu

  - name: postgress
    type: db
    host: us

  - name: redis
    type: db
    host: us   
`)

type DMZ struct {
    Applications []*Applications `yaml:"applications,omitempty"`
    Services     []*Services     `yaml:"services,omitempty"`
}

type Applications struct {
    Name string
    Type string
    Src  string `yaml:"src,omitempty"`
    use  []Use  `yaml:"use,omitempty"`
}
type Services struct {
    Name string
    Type string
    Host string `yaml:"host,omitempty"`
}
type Use struct {
    Name string `yaml:"name,omitempty"`
    host string `yaml:"host,omitempty"`
    Type string `yaml:"type,omitempty"`
}

func main() {
    dm := &DMZ{}
    result, err := dm.Parse(dmz)
    dm.getApp("app1")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(result)
    fmt.Println(dm.getApp("app2"))
    fmt.Println(dm.GetServiceForApp("mongo"))
}

func (dmz *DMZ) getApps() []*Applications {
    return dmz.Applications
}

func (dmz *DMZ) getServices() []*Services {
    return dmz.Services
}

func (dmz *DMZ) getApp(appname string) *Applications {
    for _, value := range dmz.Applications {
        if appname == value.Name {
            fmt.Println((*value).Name)
            return value
        }
    }
    return nil
}

func (dmz *DMZ) GetServiceForApp(appname string) *Services {
    for _, value := range dmz.Services {
        if appname == value.Name {
            return value
        }
    }
    return nil
}

// Parse file
func (dmz *DMZ) Parse(yamlContent []byte) (out *DMZ, err error) {
    err = yaml.Unmarshal([]byte(yamlContent), &dmz)
    if err != nil {
        log.Fatal("Yaml file is not valid, Error: " + err.Error())
    }
    return dmz, err
}

Playground 上的工作代码

如果你想让你的代码更干净,那么你也可以跳过从 parse 函数返回的结构。由于我们正在传递一个指针类型的接收器并将原始结构更新为:

package main

import (
    "fmt"
    "log"

    yaml "gopkg.in/yaml.v2"
)

var dmz = []byte(`
applications:
  - name: app1
    type: php
    src: /app1
    host: us
    use: 
      - redis
      - mysql

  - name: app2
    type: rust
    src: /app2
    host: eu
    use: 
      - mongo
      - mysql

  - name: app3
    type: golang
    src: /app3
    host: us
    use: 
      - postgress
      - mysql


services:
  - name: mongo
    type: db
    host: us

  - name: mysql
    type: db
    host: eu

  - name: postgress
    type: db
    host: us

  - name: redis
    type: db
    host: us   
`)

type DMZ struct {
    Applications []*Applications `yaml:"applications,omitempty"`
    Services     []*Services     `yaml:"services,omitempty"`
}

type Applications struct {
    Name string
    Type string
    Src  string `yaml:"src,omitempty"`
    use  []Use  `yaml:"use,omitempty"`
}
type Services struct {
    Name string
    Type string
    Host string `yaml:"host,omitempty"`
}
type Use struct {
    Name string `yaml:"name,omitempty"`
    host string `yaml:"host,omitempty"`
    Type string `yaml:"type,omitempty"`
}

func main() {
    dm := &DMZ{}
    dm.Parse(dmz)
    fmt.Println(dm.getApp("app2"))
    fmt.Println(dm.GetServiceForApp("mongo"))
}

func (dmz *DMZ) getApps() []*Applications {
    return dmz.Applications
}

func (dmz *DMZ) getServices() []*Services {
    return dmz.Services
}

func (dmz *DMZ) getApp(appname string) *Applications {
    for _, value := range dmz.Applications {
        if appname == value.Name {
            fmt.Println((*value).Name)
            return value
        }
    }
    return nil
}

func (dmz *DMZ) GetServiceForApp(appname string) *Services {
    for _, value := range dmz.Services {
        if appname == value.Name {
            return value
        }
    }
    return nil
}

// Parse file
func (dmz *DMZ) Parse(yamlContent []byte) {
    if err := yaml.Unmarshal([]byte(yamlContent), &dmz); err != nil {
        log.Fatal("Yaml file is not valid, Error: " + err.Error())
    }
}

我们注意到 Parse 函数将变得更加干净,因为我们没有从它返回任何东西,我们只是使用方法 receiver 更新原始结构,这是实现您一直试图实现的目标的更好方法。

您还可以选择通过将结构中的方法定义为接收器来实现接口(interface),您将尝试为其实现接口(interface):

package main

import (
    "fmt"
    "log"

    yaml "gopkg.in/yaml.v2"
)

type DI interface {
    GetApps() []*Applications
    GetServices() *Services
}

var dmz = []byte(`
applications:
  - name: app1
    type: php
    src: /app1
    host: us
    use: 
      - redis
      - mysql

  - name: app2
    type: rust
    src: /app2
    host: eu
    use: 
      - mongo
      - mysql

  - name: app3
    type: golang
    src: /app3
    host: us
    use: 
      - postgress
      - mysql


services:
  - name: mongo
    type: db
    host: us

  - name: mysql
    type: db
    host: eu

  - name: postgress
    type: db
    host: us

  - name: redis
    type: db
    host: us   
`)

type DMZ struct {
    Applications []*Applications `yaml:"applications,omitempty"`
    Services     []*Services     `yaml:"services,omitempty"`
}

type Applications struct {
    Name string
    Type string
    Src  string `yaml:"src,omitempty"`
    use  []Use  `yaml:"use,omitempty"`
}
type Services struct {
    Name string
    Type string
    Host string `yaml:"host,omitempty"`
}
type Use struct {
    Name string `yaml:"name,omitempty"`
    host string `yaml:"host,omitempty"`
    Type string `yaml:"type,omitempty"`
}

func main() {
    dm := &DMZ{}
    dm.Parse(dmz)
    fmt.Println(dm.getApp("app2"))
    fmt.Println(dm.GetServiceForApp("mongo"))
}

func (dmz *DMZ) GetApps() []*Applications {
    return dmz.Applications
}

func (dmz *DMZ) GetServices() []*Services {
    return dmz.Services
}

func (dmz *DMZ) getApp(appname string) *Applications {
    for _, value := range dmz.Applications {
        if appname == value.Name {
            fmt.Println((*value).Name)
            return value
        }
    }
    return nil
}

func (dmz *DMZ) GetServiceForApp(appname string) *Services {
    for _, value := range dmz.Services {
        if appname == value.Name {
            return value
        }
    }
    return nil
}

// Parse file
func (dmz *DMZ) Parse(yamlContent []byte) {
    if err := yaml.Unmarshal([]byte(yamlContent), &dmz); err != nil {
        log.Fatal("Yaml file is not valid, Error: " + err.Error())
    }
}

注意:

Generics are convenient but they come at a cost in complexity in the type system and run-time. We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it. Meanwhile, Go's built-in maps and slices, plus the ability to use the empty interface to construct containers (with explicit unboxing) mean in many cases it is possible to write code that does what generics would enable, if less smoothly.

关于go - 创建需要其他可重复逻辑作为先决条件的函数(干净的代码),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52572681/

相关文章:

language-agnostic - 注入(inject)很少使用的服务 - 构造函数与方法

Spring DI applicationContext.xml xsi :schemaLocation used?到底是怎么回事

java - 如何使用后面的代码创建自定义注释

java - Java 中的 Collection 和 Set 接口(interface)有什么区别?

go - 从go中的另一个函数调用变量

go - 即使不再维护,我还能继续使用 mgo 吗?

go - 如何从 json 中删除字段?

go - 模拟 bufio.Scanner 输入

c# - 使用简单注入(inject)器的不同配置

delphi - Delphi中的接口(interface)多态性