Cách hợp lý để bố trí dự án Go [đã đóng]


113

Tôi có một dự án bắt đầu trở nên phức tạp hơn và muốn sắp xếp hệ thống tệp theo cách như vậy để giảm bớt sự cố.

Có một số ví dụ điển hình về những gì có ý nghĩa không?

Câu trả lời:


132

Cập nhật tháng 5 năm 2013: tài liệu chính thức có trong phần " Tổ chức mã "

Mã Go phải được giữ trong không gian làm việc .
Không gian làm việc là một hệ thống phân cấp thư mục với ba thư mục ở gốc:

  • src chứa các tệp nguồn Go được tổ chức thành các gói (một gói cho mỗi thư mục),
  • pkg chứa các đối tượng gói và
  • bin chứa các lệnh thực thi.

Các go toolgói nguồn xây dựng và cài đặt các tệp nhị phân kết quả vào thư mục pkgbin.

Thư srcmục con thường chứa nhiều kho lưu trữ kiểm soát phiên bản (chẳng hạn như cho Git hoặc Mercurial) theo dõi sự phát triển của một hoặc nhiều gói nguồn.

bin/
    streak                         # command executable
    todo                           # command executable
pkg/
    linux_amd64/
        code.google.com/p/goauth2/
            oauth.a                # package object
        github.com/nf/todo/
            task.a                 # package object
src/
    code.google.com/p/goauth2/
        .hg/                       # mercurial repository metadata
        oauth/
            oauth.go               # package source
            oauth_test.go          # test source

Cập nhật tháng 7 năm 2014: xem " Cấu trúc ứng dụng đang hoạt động " từ Ben Johnson

Bài viết đó bao gồm các mẹo như:

Tách nhị phân của bạn khỏi ứng dụng của bạn

kết hợp main.gotệp và logic ứng dụng của tôi trong cùng một gói có hai hệ quả:

  • Nó làm cho ứng dụng của tôi không sử dụng được như một thư viện.
  • Tôi chỉ có thể có một ứng dụng nhị phân.

Cách tốt nhất tôi đã tìm ra để khắc phục điều này là chỉ cần sử dụng một cmdthư mục “” trong dự án của tôi, nơi mỗi thư mục con của nó là một tệp nhị phân ứng dụng.

camlistore/
  cmd/
    camget/
      main.go
    cammount/
      main.go
    camput/
      main.go
    camtool/
      main.go

Thư viện phát triển theo định hướng

Di chuyển main.gotệp ra khỏi thư mục gốc của bạn cho phép bạn xây dựng ứng dụng của mình từ quan điểm của một thư viện. Hệ nhị phân ứng dụng của bạn chỉ đơn giản là một ứng dụng khách của thư viện ứng dụng của bạn.

Đôi khi bạn có thể muốn người dùng tương tác theo nhiều cách nên bạn tạo nhiều tệp nhị phân.
Ví dụ: nếu bạn có addergói “” cho phép người dùng cộng các số lại với nhau, bạn có thể muốn phát hành phiên bản dòng lệnh cũng như phiên bản web.
Bạn có thể dễ dàng làm điều này bằng cách tổ chức dự án của mình như sau:

adder/
  adder.go
  cmd/
    adder/
      main.go
    adder-server/
      main.go

Người dùng có thể cài đặt các tệp nhị phân ứng dụng “adder” của bạn với “go get” bằng cách sử dụng dấu ba chấm:

$ go get github.com/benbjohnson/adder/...

Và thì đấy, người dùng của bạn đã cài đặt “ adder” và “ adder-server”!

Đừng phát điên với các gói con

Thông thường, các loại dự án của tôi đều rất liên quan nên nó phù hợp hơn từ quan điểm về khả năng sử dụng và API.
Những loại này cũng có thể tận dụng lợi thế của việc gọi chưa được báo cáo giữa chúng, giúp API nhỏ và rõ ràng.

  1. Nhóm các loại và mã liên quan lại với nhau trong mỗi tệp. Nếu các loại và chức năng của bạn được tổ chức tốt thì tôi thấy rằng các tệp có xu hướng nằm trong khoảng từ 200 đến 500 SLOC. Điều này nghe có vẻ nhiều nhưng tôi thấy rất dễ điều hướng. 1000 SLOC thường là giới hạn trên của tôi cho một tệp.
  2. Sắp xếp loại quan trọng nhất ở đầu tệp và thêm các loại theo mức độ quan trọng giảm dần về phía cuối tệp.
  3. Khi ứng dụng của bạn bắt đầu đạt trên 10.000 SLOC, bạn nên nghiêm túc đánh giá xem nó có thể được chia thành các dự án nhỏ hơn hay không.

Lưu ý: thực hành cuối cùng không phải lúc nào cũng tốt:

Xin lỗi tôi chỉ không thể đồng ý với thực hành này.
Việc tách loại thành tệp giúp quản lý mã, khả năng đọc, khả năng bảo trì, khả năng kiểm tra.
Nó cũng có thể đảm bảo trách nhiệm duy nhất và tuân theo nguyên tắc mở / đóng…
Quy tắc không cho phép phụ thuộc vòng tròn là buộc chúng ta phải có cấu trúc rõ ràng của các gói.


(Thay thế vào tháng 2 năm 2013, srcchỉ liên quan đến )
Bạn có thể tìm thấy bố cục cổ điển được minh họa trong " Bố cục mã GitHub ":

Ứng dụng và cả hai thư viện đều có trên Github, mỗi thư viện nằm trong kho riêng của nó.
$GOPATHlà thư mục gốc của dự án - mỗi repo Github của bạn sẽ được kiểm tra một số thư mục bên dưới $GOPATH.

Bố cục mã của bạn sẽ giống như sau:

$GOPATH/
    src/
        github.com/
            jmcvetta/
                useless/
                    .git/
                    useless.go
                    useless_test.go
                    README.md
                uselessd/
                    .git/
                    uselessd.go
                    uselessd_test.go
                    README.md

Mỗi thư mục bên dưới src/github.com/jmcvetta/là thư mục gốc của một bản kiểm tra git riêng biệt.

Tuy nhiên, điều đó đã thu hút một số lời chỉ trích, trong trang reddit này :

Tôi thực sự khuyên bạn không nên cấu trúc repo theo cách bạn có, nó sẽ bị hỏng " go get", đây là một trong những điều hữu ích nhất về cờ vây.
Tốt hơn là viết mã của bạn cho những người biết cờ vây, vì họ rất có thể là người biên dịch nó.
Và đối với những người không, ít nhất họ sẽ có cảm giác với ngôn ngữ.

Đặt gói chính vào thư mục gốc của repo.
Đặt các tài sản trong một thư mục con (để giữ mọi thứ gọn gàng).
Giữ phần thịt của mã trong một gói con (trong trường hợp bất kỳ ai muốn sử dụng lại nó bên ngoài tệp nhị phân của bạn).
Bao gồm tập lệnh thiết lập trong thư mục gốc của repo để dễ dàng tìm thấy.

Nó vẫn chỉ là một quá trình hai bước để tải xuống, xây dựng, cài đặt và thiết lập:

  • " go get <your repo path>": tải xuống và cài đặt mã go, với mã phụ cho nội dung
  • $GOPATH/<your repo path>/setup.sh: phân phối tài sản đến đúng nơi và cài đặt dịch vụ

15
Một vấn đề (lớn) setup.shlà Go hợp lý đa nền tảng trong khi các tập lệnh shell POSIX thì không.
kostix

Cấu trúc jmcvetta sẽ không bị phá vỡ go get, vì vô dụng nhập khẩu vô dụng, go get sẽ cài đặt cả hai với go get ... / nothingd. Nhưng tôi đồng ý rằng nếu thư viện vô dụng là một thư viện được thiết kế đặc biệt cho vô dụng, thì sẽ hợp lý hơn nếu giữ nó trong một git repo, dưới dạng một thư mục con hoặc anh chị em.
mna 14/02/13

@PuerkitoBio Tôi đồng ý. Việc đào tạo của tôi về kiểm soát phiên bản và quản lý dựa trên thành phần ( stackoverflow.com/a/933735/6309 ) khiến tôi hướng tới một thành phần trên mỗi repo, do đó, phần thứ hai của câu trả lời này.
VonC

7

Tôi giả định rằng với 'dự án' bạn không có nghĩa là một gói Go mà là một phần mềm bạn phát triển. Nếu không, bạn có thể nhận trợ giúp ở đâyở đây . Tuy nhiên, cách viết các gói cho Go không khác quá nhiều: Sử dụng các gói, tạo một thư mục cho mỗi gói và kết hợp các gói đó trong ứng dụng của bạn.

Để xây dựng ý kiến ​​cho mình, bạn có thể xem kho lưu trữ thịnh hành của Go trên github: https://github.com/trending/go . Ví dụ đáng chú ý là cayleyzeus .

Sơ đồ phổ biến nhất có lẽ là có một tệp Go chính và nhiều mô-đun và mô-đun con trong thư mục riêng của chúng. Trong trường hợp bạn có nhiều tệp meta (doc, giấy phép, các mẫu, ...), bạn có thể muốn đặt mã nguồn vào một thư mục con. Đó là những gì tôi đã làm cho đến nay.


@aussiegeek, tôi không phải là chuyên gia cờ vây, nhưng tôi đã áp dụng thành công những gì nemo đề xuất trong mã của riêng tôi - ý tưởng là bạn có thể có các mô-đun trong thư mục dự án của mình, bạn chỉ cần tham khảo chúng bằng cách sử dụng tiền tố đầy đủ của chúng - tương đối đến $GOPATH/srchoặc sử dụng họ go gettên bảng được.
kostix 14/02/13

doozerdkhông phải là một ví dụ tốt, ngay cả các bài kiểm tra của nó cũng yếu.
Inanc Gumus

@InancGumus Tôi khuyến khích bạn đề xuất một ví dụ tốt hơn.
nemo


1

Có một cách tiếp cận được đề xuất từ các tác giả của Golang xác định cách bố trí mã của bạn để hoạt động tốt nhất với các công cụ go và hỗ trợ hệ thống kiểm soát nguồn


1
Đó là cách sắp xếp $GOROOT, không phải mã trong src/<project>thư mục.
docwhat

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.