dplece1882 2018-09-29 21:25
浏览 29
已采纳

创建需要其他可重复逻辑作为前提条件的函数(纯净代码)

I've the following yaml file which I need to parse (the parse is working as expected) and need to provide a datafrom the yaml file content that should be exposed by the following decoupled functions

I need to provide the following functions (here is example of some of those functions, need more with the same pattern...)

getApps()

getServices()

GetApp(appname)

GetServiceForApp(appname)

This is the code (which works...)

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   
`)

This is the structs

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
}

As the Parse function is a per-requite to all the needed functions (which I listed above) I wonder how is the best to create them ,create simple function that every time call to the parse function and then do the logic (not a problem) but I wonder if there is better approach which follows the clean code principles for Golang , with 'interface / dependency injections' ?

UPDATE:

I want to avoid doing things like following, assume that I need to call to those function from different packages or even different GitHub repository how it's best to do it with Golang clean code.

func getApps(){

 dmz := Parse()
....
}


func getServices(){

 dmz := Parse()
....

}


func getApp(appname string){

 dmz := Parse()
....

}


func GetServiceForApp(appname string){

 dmz := Parse()
....

}

And I need more functions with the same pattern ...

What I need some Clean Code solution using interface/dependency injection like a best practice code example in Golang

If something is not clear please let me know :)

  • 写回答

3条回答 默认 最新

  • dsndm82062 2018-10-03 19:08
    关注

    Parse the value inside pointer type struct value which will store the value inside the struct pointed by a pointer. Then Create method receives on All the methods from which you want to get the value of an app or service inside the struct.

    Using pointer receiver in all methods you will be able to access the original struct which is updated when parsing the yaml.

    Create a pointer to empty instance of the struct and pass it as a method receiver. Then access that struct in unmarshal to update yaml data in original struct. Access the updated struct in every function by accessing the value of original struct using pointer receiver to method.

    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
    }
    

    Working code on Playground

    If you want your code to be more clean then you can also skip the struct returned from parse function. Since we are passing a pointer type receiver and updating the original struct as:

    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())
        }
    }
    

    As we can notice that Parse function will become more clean since we are not returning anything from it we are just updating the original struct using method receiver which is a much better way to achieve what you have been trying to achieve.

    You can also choose to implement an interface by defining the methods in struct as receiver for which you are trying to implement the interface as:

    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())
        }
    }
    

    Note:

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 求京东批量付款能替代天诚
  • ¥15 slaris 系统断电后,重新开机后一直自动重启
  • ¥15 51寻迹小车定点寻迹
  • ¥15 谁能帮我看看这拒稿理由啥意思啊阿啊
  • ¥15 关于vue2中methods使用call修改this指向的问题
  • ¥15 idea自动补全键位冲突
  • ¥15 请教一下写代码,代码好难
  • ¥15 iis10中如何阻止别人网站重定向到我的网站
  • ¥15 滑块验证码移动速度不一致问题
  • ¥15 Utunbu中vscode下cern root工作台中写的程序root的头文件无法包含