myapp/ main.go server.go user.go lesson.go course.go
Go flat 프로젝트 구조 이해에 입문하면 거의 모든 사람이 플랫한 애플리케이션 구조로 시작합니다. Go flat 프로젝트 구조 이해 투어의 모든 프로그램, Go flat 프로젝트 구조 이해phercises의 대부분의 연습 및 기타 많은 초기 Go flat 프로젝트 구조 이해 프로그램은 어떤 패키지로도 분할되지 않습니다. 대신, 몇 개의 .go
파일을 만들고 모든 코드를 동일한(보통 main
) 패키지에 넣습니다.
관련 학습 권장사항: Go flat 프로젝트 구조 이해 언어 튜토리얼
플랫 구조를 사용할 때에도 코딩 모범 사례를 준수해야 합니다. 애플리케이션의 여러 부분을 분리하려면 서로 다른 .go
파일을 사용해야 합니다. .go
文件放置在一个软件包中。
myapp / main.go#阅读配置并在此处启动您的应用 server.go#总体HTTP处理逻辑在这里 user_handler.go#用户http处理程序逻辑在这里 user_store.go#用户数据库逻辑在这里 # 等等...
进入Go flat 프로젝트 구조 이해时,几乎每个人都从一个平面应用程序结构开始。 Go flat 프로젝트 구조 이해 tour中的每个程序,Go flat 프로젝트 구조 이해phercises中的大多数练习以及许多其他早期的Go flat 프로젝트 구조 이해程序都没有被分解成任何包装。取而代之的是,我们只创建几个.go
文件,然后将所有代码放入相同的(通常是main
)包中。
起初,这听起来很糟糕。代码会很快变得笨拙吗?如何将业务逻辑与UI渲染代码分开?我如何找到正确的源文件?毕竟,我们使用软件包的很大一部分原因是要分离关注点,同时使更容易快速地导航到正确的源文件。
相关学习推荐:Go flat 프로젝트 구조 이해语言教程
使用平面结构时,您仍应尝试遵守编码最佳实践。您将需要使用不同的.go
文件分隔应用程序的不同部分:
type Server struct { apiClient *someapi.Client router *some.Router } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.router.ServeHTTP(w, r) }
全局变量仍然可能成为问题,因此您应考虑将类型与方法配合使用,以使它们脱离代码:
//警告:此示例非常人为设计,甚至可能无法编译。 type Config struct { SomeAPIKey string Port string EnableTheThing bool } func main() { var config Config config.SomeAPIKey = os.Getenv("SOMEAPI_KEY") config.Port = os.Getenv("MYAPP_PORT") if config.Port == "" { config.Port = "3000" } config.EnableTheThing = true if err := run(config); err != nil { log.Fatal(err) } } func run(config Config) error { server := myapp.Server{ APIClient: someapi.NewClient(config.SomeAPIKey), } return http.ListenAndServe(":" + config.Port, server) }
而且您的main()
函数可能仍应在设置应用程序之外删除大多数逻辑:
myapp/ cmd/ web/ # package main main.go cli/ # package main main.go # package myapp server.go user_handler.go user_store.go ...
实际上,您实际上可以将基本上是平面结构的代码全部使用在一个软件包中,并在单独的main
软件包中定义命令。这将允许您使用常见的cmd
子目录模式:
输入JsonCourse struct { ID字符串`json:“ id”` 价格结构{ USD字符串`json:“ usd”` }`json:“价格”` }
在此示例中,您的应用程序基本上仍然是平坦的,但是您拔出了main
软件包是因为您有需要-例如可能需要使用同一核心应用程序来支持两个命令。
扁平结构的主要好处不是将所有代码都保存在一个目录中,也不是那样愚蠢的东西。这种结构的核心好处是您可以不必担心如何组织事物,而可以继续解决您打算通过应用程序解决的问题。
我绝对喜欢这个应用程序结构让我回想起PHP的日子。当我第一次学习编码时,我开始使用随机PHP文件,其逻辑与各种HTML混合在一起,这真是一团糟。我并不是在建议我们以大型应用程序的方式构建-那样会很糟糕-但是我并不担心一切都应该放在哪里,而是更加关注学习如何编写代码和解决我的特定问题。无论您是要了解应用程序的需求,您的域还是一般的编码方式,使用扁平结构都可以使您更轻松地专注于学习和构建。
这是正确的,因为我们可以不再担心诸如“这种逻辑应该去哪里?”之类的问题。因为如果我们犯了一个错误,很容易解决。如果它是一个函数,我们可以将其移动到包中的任何新源文件中。如果它是错误类型的方法,我们可以创建两个新类型并将逻辑与原始类型分开。有了这些,我们就不必担心会遇到奇怪的周期性依赖问题,因为我们只有一个软件包。
考虑平面结构的另一个重要原因是,随着应用程序复杂性的提高,结构的发展变得容易得多。当您明显可以从将代码拆分到一个单独的程序包中受益时,您通常需要做的就是将一些源文件移到一个子目录中,更改其程序包,并更新任何引用以使用新的程序包前缀。例如,如果我们有SqlUser
并决定从一个单独的sql
包中处理所有与数据库相关的逻辑中受益,我们将更新所有引用以现在使用sql.User将类型移动到新软件包后
。我发现,像MVC这样的结构在重构方面更具挑战性,尽管并非没有其他编程语言那样困难或困难。
扁平结构对于通常太快无法创建包的初学者特别有用。我真的不能说为什么会发生这种现象,但是Go flat 프로젝트 구조 이해的新手喜欢创建大量的软件包,这几乎总是导致口吃(user.User
type SqlCourse struct { ID int Price int }전역 변수는 여전히 문제가 될 수 있으므로 코드에 포함되지 않도록 메소드가 있는 유형을 사용하는 것을 고려해야 합니다. :
func (sc SqlCourse) ToJson() (JsonCourse, error) { jsonCourse := JsonCourse{ ID: fmt.Sprintf("crs_%v", sc.ID), } jsonCourse.Price.USD = Price: fmt.Sprintf("%d.%2d", sc.Price/100, sc.Price%100) return jsonCourse, nil }그리고
main()
함수는 아마도 애플리케이션 설정 이외의 로직 대부분을 제거해야 할 것입니다. func (jc JsonCourse) ToSql() (SqlCourse, error) { var sqlCourse SqlCourse // JSON ID is "crs_123" and we convert to "123" // for SQL IDs id, err := strconv.Atoi(strings.TrimPrefix(jc.ID, "crs_")) if err != nil { // Note: %w is a Go flat 프로젝트 구조 이해 1.13 thing that I haven't really // tested out, so let me know if I'm using it wrong실제로 전체 코드를 기본적으로 평면 구조로 만들 수도 있습니다. 패키지에 포함하고 별도의
main
패키지에 명령을 정의합니다. 이렇게 하면 일반적인 cmd
하위 디렉터리 패턴을 사용할 수 있습니다: 🎜rrreee🎜 이 예에서 애플리케이션은 기본적으로 여전히 플랫하지만 main
를 꺼냈습니다. 패키지는 필요가 있습니다. 예를 들어 동일한 핵심 애플리케이션을 사용하여 두 개의 명령을 지원해야 할 수도 있습니다. 🎜SqlUser
가 있고 별도의 sql
패키지에서 모든 데이터베이스 관련 논리를 처리하는 이점을 누리기로 결정한 경우 이제 sql을 사용하도록 모든 참조를 업데이트합니다. .사용자 유형은
이후 새 패키지로 이동되었습니다. MVC와 같은 구조는 다른 프로그래밍 언어만큼 어렵거나 어렵지는 않지만 리팩토링하기가 더 어렵다는 것을 알았습니다. 🎜🎜플랫 구조는 패키지를 너무 빨리 만드는 초보자에게 특히 유용합니다. 실제로 왜 이런 일이 발생하는지 말할 수는 없지만 Go flat 프로젝트 구조 이해 초보자는 수많은 패키지를 만드는 것을 좋아하는데, 이로 인해 거의 항상 끊김 현상(user.User
), 순환 종속성 또는 기타 문제가 발생합니다. 🎜🎜🎜 MVC에 대한 다음 기사에서는 너무 많은 패키지를 생성하는 현상이 Go flat 프로젝트 구조 이해에서는 그렇지 않더라도 MVC가 불가능해 보일 수 있는 방식을 살펴보겠습니다. 🎜🎜通过推迟创建新程序包的决定,直到我们的应用程序增长一点并更好地了解它,发芽的Go flat 프로젝트 구조 이해phers犯此错误的可能性就大大降低了。
这也是为什么很多人会鼓励开发人员避免过早将其代码分解到微服务中的原因-您通常没有足够的知识来真正知道应该和不应该将哪些内容分解为微服务以及抢先式微服务( I kinda希望能成为一句俗语)只会在将来带来更多工作。
假装使用扁平结构没有任何不利之处,这对我来说是不诚实的,所以我们也应该讨论这些。
对于初学者来说,扁平的结构只能使您受益匪浅。它会工作一段时间(可能比您想象的更长),但是到某个时候,您的应用程序将变得足够复杂,您需要开始分解它。使用平面结构的好处是您可以推迟使用它,并且在分解时可能会更好地理解您的代码。缺点是,您将需要花一些时间进行重构,并且您可能(也许-但这很麻烦)发现自己已经重构为您想从任何地方开始的结构。
使用平面结构时,命名冲突有时也会很尴尬。例如,假设您想要在应用程序中使用Course
类型,但是在数据库中表示课程的方式与在JSON中呈现课程的方式不同。一个快速的解决方案是创建两种类型,但是由于它们都在同一个包中,因此每种类型都需要使用不同的名称,并且可能最终以类似以下内容的形式出现:SqlCourse
和JsonCourse
。这确实没什么大不了的,但是有点令人遗憾的是我们最终得到了零类型,简单地称为Course
。
将代码重构为新程序包也不总是那么简单。是的,这通常很容易,但是由于所有代码都在一个包中,因此您有时可能会遇到天生具有周期性的代码。例如,假设我们的课程是否具有在JSON响应中始终以crs_
开头的ID,并且我们想以各种货币返回价格。我们可以创建一个JsonCourse
来处理:
输入JsonCourse struct { ID字符串`json:“ id”` 价格结构{ USD字符串`json:“ usd”` }`json:“价格”` }
同时,SqlCourse
仅需要存储一个整数ID和一个以美分为单位的单一价格,我们可以使用各种货币对其进行格式化。
type SqlCourse struct { ID int Price int }
现在我们需要一种将SqlCourse
转换为JsonCourse
的方法,因此我们可以将其作为SqlCourse
类型的方法:
func (sc SqlCourse) ToJson() (JsonCourse, error) { jsonCourse := JsonCourse{ ID: fmt.Sprintf("crs_%v", sc.ID), } jsonCourse.Price.USD = Price: fmt.Sprintf("%d.%2d", sc.Price/100, sc.Price%100) return jsonCourse, nil }
然后稍后我们可能需要一种方法来解析传入的JSON并将其转换为SQL等效项,因此我们将其添加到JsonCourse
类型中作为另一种方法:
func (jc JsonCourse) ToSql() (SqlCourse, error) { var sqlCourse SqlCourse // JSON ID is "crs_123" and we convert to "123" // for SQL IDs id, err := strconv.Atoi(strings.TrimPrefix(jc.ID, "crs_")) if err != nil { // Note: %w is a Go flat 프로젝트 구조 이해 1.13 thing that I haven't really // tested out, so let me know if I'm using it wrong
相关学习推荐:编程视频
위 내용은 Go flat 프로젝트 구조 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!