一尘不染

在GO中使用构建器的继承

go

我需要为每种构建类型创建构建器(基础)和特定的构建器。

e.g.

builder for html project
builder for node.js project
builder for python project
builder for java project

…。

主要功能如下:

File:Builder.go

接口

type Builder interface {
    Build(string) error
}

文件:nodebuilder.go

//This is the struct ???? not sure what to put here...
type Node struct {


}


func (n Node) Build(path string) error {

//e.g. Run npm install which build's nodejs projects

        command := exec.Command("npm", "install")
        command.Dir = “../path2dir/“

        Combined, err := command.CombinedOutput()

        if err != nil {
            log.Println(err)
        }
        log.Printf("%s", Combined)
    }

    ...
    //return new(error)
}

主要假设/过程:

  1. 要开始在每个模块上构建,我需要获取它的路径
  2. 我需要将模块复制到临时文件夹
  3. 我需要在其上运行构建(实现构建界面,如此类mvn build npm install
  4. 构建完成后,用dep压缩模块
  5. 将其复制到新的目标文件夹

注意:除了build和之后path(应专门处理),所有其他功能都相同,
例如 zip copy

  1. 我应该将放置在zip and copy(结构中)的哪个位置,例如,应该如何实现它们并将它们传递给构建器?

  2. 我是否应该根据假设对项目进行结构设计?


阅读 238

收藏
2020-07-02

共1个答案

一尘不染

SOLID 的第一条原则说一段代码应该只承担一个责任。

发生在该背景下,它确实是没有意义的,任何builder关心copyzip构建过程的一部分。这超出了builder责任。即使使用合成(嵌入)也不够整洁。

Builder顾名思义,缩小范围,其核心职责是构建代码。但更具体地说,Builder的责任是在路径上构建代码。什么路 最惯用的方法是
当前路径工作目录 。这会在接口中添加两个方法:Path() string返回 当前路径
ChangePath(newPath string) error更改 当前路径
。该方法很简单,保留一个字符串字段,因为当前路径通常可以完成此工作。而且它可以轻松地扩展到某些远程过程。

如果我们仔细地看一下,这无疑是两个 构建 概念。一个是整个构建 过程 ,从制作temp目录到将其复制回去,全部五个步骤。另一个是build
命令 ,这是该过程的第三步。

那是非常鼓舞人心的。就像传统的程序编程一样,过程是作为功能呈现的过程。因此,我们编写了一个Build函数。它序列化了所有5个步骤,简单而又简单。

码:

package main

import (
    "io/ioutil"
)

//A builder is what used to build the language. It should be able to change working dir.
type Builder interface {
    Build() error //Build builds the code at current dir. It returns an error if failed.
    Path() string //Path returns the current working dir.
    ChangePath(newPath string) error //ChangePath changes the working dir to newPath.
}

//TempDirFunc is what generates a new temp dir. Golang woould requires it in GOPATH, so make it changable.
type TempDirFunc func() string

var DefualtTempDirFunc = func() string {
    name,_ := ioutil.TempDir("","BUILD")
    return name
}

//Build builds a language. It copies the code to a temp dir generated by mkTempdir
//and call the Builder.ChangePath to change the working dir to the temp dir. After
//the copy, it use the Builder to build the code, and then zip it in the tempfile,
//copying the zip file to `toPath`.
func Build(b Builder, toPath string, mkTempDir TempDirFunc) error {

    if mkTempDir == nil {
        mkTempDir = DefaultTempDirFunc
    }

    path,newPath:=b.Path(),mkTempDir()
    defer removeDir(newPath) //clean-up

    if err:=copyDir(path,newPath); err!=nil {
        return err
    }
    if err:=b.ChangePath(newPath) !=nil {
        return err
    }

    if err:=b.Build(); err!=nil {
        return err
    }

    zipName,err:=zipDir(newPath) // I don't understand what is `dep`.
    if err!=nil { 
        return err
    }

    zipPath:=filepath.Join(newPath,zipName)
    if err:=copyFile(zipPath,toPath); err!=nil {
        return err
    }


    return nil
}

//zipDir zips the `path` dir and returns the name of zip. If an error occured, it returns an empty string and an error.
func zipDir(path string) (string,error) {}

//All other funcs is very trivial.

大部分的东西都涵盖在评论,我真的很砍伐懒得所有写那些copyDir/
removeDir东西。在设计部分中没有提到的一件事是mkTempDir功能。如果代码位于/tmp/xxx/之外GOPATH,那么Golang会感到不满意,并且由于更改代码GOPATH会破坏导入路径服务,因此更改时会遇到更多麻烦,因此golang需要使用唯一的函数才能在中生成临时目录GOPATH

编辑:

哦,我忘了说一件事。像这样处理错误是非常丑陋和不负责任的。但是这个想法就在那里,而且更体面的错误处理主要需要使用内容。因此,请自己进行更改,记录日志,出现紧急情况或您想要的任何内容。

编辑2:

您可以按以下方式重新使用您的npm示例。

type Node struct {
    path string
}

func (n Node) Build(path string) error {
    //e.g. Run npm install which build's nodejs project
    command := exec.Command("npm", "install")
    command.Dir = n.path
    Combined, err := command.CombinedOutput()
    if err != nil {
        log.Println(err)
    }
    log.Printf("%s", Combined)
    return nil
}

func (n *Node) ChangePath(newPath string) error {
    n.path = newPath
}

func (n Node) Path() string {
    return n.path
}

并将其与其他语言结合在一起:

func main() {
    path := GetPathFromInput()
    switch GetLanguageName(path) {
    case "Java":
        Build(&Java{path},targetDirForJava(),nil)
    case "Go":
        Build(&Golang{path,cgoOptions},targetDirForGo(),GoPathTempDir()) //You can disable cgo compile or something like that.
    case "Node":
        Build(&Node{path},targetDirForNode(),nil)
    }
}

一种技巧是获取语言名称。GetLanguageName应该返回代码path所使用的语言的名称。这可以通过使用ioutil.ReadDir检测文件名来完成。

还要注意,尽管我使Node结构非常简单,只存储一个path字段,但是您可以轻松扩展它。就像Golang部分一样,您可以在此处添加构建选项。

编辑3:

关于包装结构:

首先,我实际上认为所有内容:Build函数,语言构建器和其他util /
helpers应该放在一个包中。它们都是为一项任务而工作:建立一种语言。没有必要,几乎没有期望将任何代码段隔离为另一个(子)程序包。

因此,这意味着一个目录。剩下的确实是一些非常个人化的风格,但我会分享我的:

我会将函数Build和接口Builder放入一个名为的文件中main.go。如果前端代码极少且可读性强,我也会将它们放入其中main.go,但是如果前端代码很长且具有某些ui逻辑,则根据实际代码将其放入front- end.goor cli.goui.goor中。

接下来,对于每种语言,我将.go使用语言代码创建一个文件。这清楚表明我可以在哪里检查它们。或者,如果代码真的很小,那么将它们全部组合到一个中也不是坏主意builders.go。毕竟,现代编辑器不仅仅具有定义结构和类型的能力。

最后,所有的copyDirzipDir功能都到了util.go。这很简单-它们是实用程序,大多数时候我们只是不想打扰它们。

2020-07-02