วิธีที่เหมาะสมในการจัดวางโครงการ Go [ปิด] คืออะไร


113

ฉันมีโปรเจ็กต์ go ที่เริ่มซับซ้อนมากขึ้นและต้องการจัดวางระบบไฟล์เพื่อลดความเจ็บปวด

มีตัวอย่างที่ดีในสิ่งที่สมเหตุสมผลหรือไม่?

คำตอบ:


132

อัปเดตพฤษภาคม 2013: เอกสารอย่างเป็นทางการอยู่ในส่วน " Code organization "

รหัสไปจะต้องเก็บไว้ภายในพื้นที่ทำงาน
เวิร์กสเปซคือลำดับชั้นของไดเร็กทอรีที่มีไดเร็กทอรีสามไดเร็กทอรีที่รูท:

  • src มีไฟล์ต้นฉบับของ Go ที่จัดเป็นแพ็คเกจ (หนึ่งแพ็คเกจต่อไดเรกทอรี)
  • pkg มีวัตถุแพคเกจและ
  • bin มีคำสั่งปฏิบัติการ

go toolสร้างแพคเกจติดตั้งแหล่งที่มาและไบนารีที่เกิดขึ้นไปpkgและbinไดเรกทอรี

โดยsrcทั่วไปไดเร็กทอรีย่อยจะมีที่เก็บการควบคุมเวอร์ชันหลายรายการ (เช่นสำหรับ Git หรือ Mercurial) ที่ติดตามการพัฒนาแพ็กเกจต้นทางอย่างน้อยหนึ่งแพ็กเกจ

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

อัปเดตกรกฎาคม 2014: ดู "การจัดโครงสร้างแอปพลิเคชันใน Go " จากBen Johnson

บทความนั้นมีเคล็ดลับเช่น:

แยกไบนารีของคุณออกจากแอปพลิเคชันของคุณ

การรวมmain.goไฟล์และตรรกะแอปพลิเคชันของฉันในแพ็คเกจเดียวกันมีผลสองประการ:

  • ทำให้แอปพลิเคชันของฉันใช้งานไม่ได้ในฐานะห้องสมุด
  • ฉันสามารถมีไบนารีแอปพลิเคชันเดียวเท่านั้น

วิธีที่ดีที่สุดที่ฉันพบในการแก้ไขปัญหานี้คือเพียงแค่ใช้cmdไดเร็กทอรี“” ในโปรเจ็กต์ของฉันโดยที่ไดเร็กทอรีย่อยแต่ละไดเร็กทอรีเป็นไบนารีของแอปพลิเคชัน

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

ห้องสมุดขับเคลื่อนการพัฒนา

การย้ายmain.goไฟล์ออกจากรูทช่วยให้คุณสร้างแอปพลิเคชันของคุณจากมุมมองของไลบรารี ไบนารีแอปพลิเคชันของคุณเป็นเพียงไคลเอนต์ของไลบรารีแอปพลิเคชันของคุณ

บางครั้งคุณอาจต้องการให้ผู้ใช้โต้ตอบได้หลายวิธีดังนั้นคุณจึงต้องสร้างไบนารีหลายรายการ
ตัวอย่างเช่นหากคุณมีadderแพ็กเกจ“” ที่ให้ผู้ใช้เพิ่มหมายเลขเข้าด้วยกันคุณอาจต้องการปล่อยเวอร์ชันบรรทัดคำสั่งและเวอร์ชันสำหรับเว็บ
คุณสามารถทำได้อย่างง่ายดายโดยจัดโครงการของคุณดังนี้:

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

ผู้ใช้สามารถติดตั้งไบนารีแอปพลิเคชัน“ adder” ด้วย“ go get” โดยใช้จุดไข่ปลา:

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

และ voila ผู้ใช้ของคุณได้ติดตั้ง“ adder” และ“ adder-server” ไว้แล้ว!

อย่าไปคลั่งไคล้กับแพ็กเกจย่อย

โดยปกติประเภทของโครงการของฉันจะเกี่ยวข้องกันมากดังนั้นจึงเหมาะกับการใช้งานและมุมมองของ API มากกว่า
ประเภทเหล่านี้ยังสามารถใช้ประโยชน์จากการโทรที่ไม่ได้ส่งออกระหว่างพวกเขาซึ่งทำให้ API มีขนาดเล็กและชัดเจน

  1. จัดกลุ่มประเภทและรหัสที่เกี่ยวข้องเข้าด้วยกันในแต่ละไฟล์ หากประเภทและฟังก์ชันของคุณได้รับการจัดระเบียบอย่างดีฉันพบว่าไฟล์มักจะอยู่ระหว่าง 200 ถึง 500 SLOC อาจฟังดูเหมือนมาก แต่ฉันคิดว่ามันง่าย 1,000 SLOC มักจะเป็นขีด จำกัด สูงสุดของฉันสำหรับไฟล์เดียว
  2. จัดระเบียบประเภทที่สำคัญที่สุดที่ด้านบนของไฟล์และเพิ่มประเภทที่ลดความสำคัญลงที่ด้านล่างของไฟล์
  3. เมื่อแอปพลิเคชันของคุณเริ่มได้รับ SLOC มากกว่า 10,000 รายการคุณควรประเมินอย่างจริงจังว่าสามารถแบ่งออกเป็นโครงการขนาดเล็กได้หรือไม่

หมายเหตุ: การปฏิบัติครั้งสุดท้ายไม่ดีเสมอไป:

ขออภัยฉันไม่เห็นด้วยกับแนวทางปฏิบัตินี้
การแยกประเภทเป็นไฟล์ช่วยในการจัดการโค้ดความสามารถในการอ่านการบำรุงรักษาการทดสอบ
นอกจากนี้ยังอาจรับประกันความรับผิดชอบเดียวและปฏิบัติตามหลักการเปิด / ปิด ...
กฎสำหรับการไม่อนุญาตให้มีการพึ่งพาแบบวงกลมคือการบังคับให้เรามีโครงสร้างที่ชัดเจนของบรรจุภัณฑ์


(ทางเลือกในเดือนกุมภาพันธ์ 2013 srcเท่านั้น)
คุณสามารถดูเค้าโครงคลาสสิกที่แสดงใน " เค้าโครงโค้ด GitHub ":

แอปและไลบรารีทั้งสองอยู่บน Github แต่ละแห่งอยู่ในที่เก็บของตัวเอง
$GOPATHเป็นรากของโครงการ - แต่ละ Repos Github ของคุณจะถูกตรวจสอบจากหลาย ๆ $GOPATHโฟลเดอร์ด้านล่าง

เลย์เอาต์โค้ดของคุณจะมีลักษณะดังนี้:

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

แต่ละโฟลเดอร์ที่อยู่ด้านล่างsrc/github.com/jmcvetta/คือรูทของการชำระเงินคอมไพล์แยกกัน

สิ่งนี้ดึงดูดการวิพากษ์วิจารณ์บางอย่างในหน้า reddit นี้ :

ฉันขอแนะนำอย่างยิ่งว่าอย่าจัดโครงสร้าง repo แบบที่คุณมีมันจะพัง " go get" ซึ่งเป็นหนึ่งในสิ่งที่มีประโยชน์ที่สุดเกี่ยวกับ Go
การเขียนโค้ดของคุณสำหรับคนที่รู้จัก Go นั้นดีกว่ามากเนื่องจากพวกเขามักจะเป็นคนที่รวบรวมมัน
และสำหรับคนที่ไม่มีอย่างน้อยพวกเขาก็จะรู้สึกถึงภาษา

ใส่แพ็กเกจหลักในรูทของ repo
มีเนื้อหาในไดเรกทอรีย่อย (เพื่อให้ทุกอย่างเรียบร้อย)
เก็บเนื้อของรหัสไว้ในแพ็กเกจย่อย (ในกรณีที่ใครก็ตามต้องการใช้ซ้ำนอกไบนารีของคุณ)
รวมสคริปต์การตั้งค่าไว้ในรูทของ repo เพื่อให้ค้นหาได้ง่าย

ยังคงเป็นเพียงขั้นตอนสองขั้นตอนในการดาวน์โหลดสร้างติดตั้งและตั้งค่า:

  • " go get <your repo path>": ดาวน์โหลดและติดตั้งโค้ด go พร้อม subdir สำหรับเนื้อหา
  • $GOPATH/<your repo path>/setup.sh: กระจายทรัพย์สินไปยังสถานที่ที่เหมาะสมและติดตั้งบริการ

15
ปัญหาหนึ่ง (ใหญ่) setup.shคือ Go เป็นข้ามแพลตฟอร์มที่สมเหตุสมผลในขณะที่เชลล์สคริปต์ POSIX ไม่ใช่
kostix

โครงสร้าง jmcvetta จะไม่ทำลาย go get เนื่องจากการนำเข้าที่ไร้ประโยชน์นั้นไร้ประโยชน์ go get จะติดตั้งทั้งสองด้วย go get ... / uselessd แต่ฉันยอมรับว่าถ้าไม่มีประโยชน์คือห้องสมุดที่สร้างขึ้นมาเพื่อไร้ประโยชน์โดยเฉพาะมันสมเหตุสมผลกว่าที่จะเก็บไว้ใน git repo เดียวเป็นโฟลเดอร์ย่อยหรือพี่น้องกัน
MNA

@PuerkitoBio ฉันเห็นด้วย การฝึกอบรมของฉันในการควบคุมเวอร์ชันและในการจัดการตามส่วนประกอบ ( stackoverflow.com/a/933735/6309 ) ทำให้ฉันได้มากกว่าหนึ่งองค์ประกอบต่อ repo ดังนั้นส่วนที่สองของคำตอบนี้
VonC

7

ฉันคิดว่าใน 'โครงการ' คุณไม่ได้หมายถึงแพ็คเกจ Go แต่เป็นซอฟต์แวร์ที่คุณพัฒนา มิฉะนั้นคุณจะได้รับความช่วยเหลือที่นี่และที่นี่ อย่างไรก็ตามการเขียนแพ็คเกจสำหรับ Go ไม่แตกต่างกันมากนัก: ใช้แพ็คเกจสร้างโฟลเดอร์สำหรับแต่ละแพ็คเกจและรวมแพ็คเกจเหล่านั้นในแอปพลิเคชันของคุณ

เพื่อสร้างตัวเองความเห็นคุณสามารถดูแนวโน้มที่เก็บไปบน GitHub: https://github.com/trending/go ตัวอย่างที่โดดเด่น ได้แก่เคย์ลีย์ และซีอุ

รูปแบบที่ได้รับความนิยมมากที่สุดอาจจะมีไฟล์ Go หลักและโมดูลและโมดูลย่อยจำนวนมากในไดเร็กทอรีของตนเอง ในกรณีที่คุณมีไฟล์เมตาจำนวนมาก (doc, license, template, ... ) คุณอาจต้องการใส่ซอร์สโค้ดลงในไดเร็กทอรีย่อย นั่นคือสิ่งที่ฉันทำจนถึงตอนนี้


@aussiegeek ฉันไม่ใช่ผู้เชี่ยวชาญ Go แต่ฉันใช้สิ่งที่ nemo เสนอในโค้ดของตัวเองได้สำเร็จ - แนวคิดคือคุณสามารถมีโมดูลภายใต้ไดเรกทอรีโครงการของคุณคุณเพียงแค่อ้างถึงพวกเขาโดยใช้คำนำหน้าแบบเต็ม - ญาติ ไป$GOPATH/srcหรือใช้ของพวกเขาgo getชื่อโต๊ะ
kostix

doozerdไม่ใช่ตัวอย่างที่ดีแม้การทดสอบจะอ่อนแอ
Inanc Gumus

@InancGumus ฉันขอแนะนำให้คุณแนะนำตัวอย่างที่ดีกว่า
nemo

ดูนี้และนี้
Inanc Gumus

1

มีแนวทางที่แนะนำจากผู้เขียนของ Golang ซึ่งกำหนดวิธีการจัดวางโค้ดของคุณเพื่อให้ทำงานได้ดีที่สุดกับเครื่องมือ go และเพื่อสนับสนุนระบบควบคุมแหล่งที่มา


1
นั่นคือวิธีการจัดวาง$GOROOTไม่ใช่รหัสภายในsrc/<project>ไดเรกทอรี
docwhat

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.