jjzjj

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

coder 2023-06-27 原文

我有以下 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/

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

  1. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  2. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  5. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  6. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  7. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  8. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  9. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  10. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

随机推荐