دستور پخت یه برنامه خوب در گولنگ

دستور پخت یه برنامه خوب در گولنگ

بله درست خوندین توی این نوشته میخوایم دستور پخت یه برنامه خوب گولنگی رو با هم مرور کنیم.

هدف ما از آشپزی پختن یک غذای زیبا و خوشمزه هست. هدف از برنامه نویسی هم نوشتن برنامه‌ای است که تمیز و خروجی مناسب را تولید کند.

همانطور که در آشپزی دستورالعمل‌هایی وجود داره، در برنامه‌نویسی نیز دستورات و قواعدی  برای بهتر نوشتن برنامه وجود دارند. که توی این مقاله با هم یاد میگیریم چطوری برنامه گولنگی بهتری بنویسیم.

لیست خرید

خب قبل از شروع کار ما باید یه سری مواد اولیه تهیه کنیم این موارد میتونید توی این لیست پیدا کنین.

ساختار


ساختار پروژه

هر پروژه ای شامل دایرکتور ها و فایل میشه، ما میتونیم با استفاده از ساختار زیر اسکلت برنامه تحت وبمون رو پیاده سازی کنیم(البته میتونیم با استفاده از معماری ای که داخل برناممون  استفاده میکنیم این ساختار تغیر بدیم).

- templates (views)          # Template files
- public (static)            # Public assets
	- css
	- fonts
	- img
	- js
- routes
- models
- pkg
	- setting                 # Global settings of app
- cmd                         # Files of commands
- conf                        # Default configuration
	- locale                  # I18n locale files
- custom                      # Custom configuraion
- data                        # App generated data
- log                         # App generated log

خط فرمان

اگر توی برنامه ای قصد پیاده سازی کامند لاین داریم، هر دستور باید در یک فایل جداگانه توی فولدر cmd قرار بگیره مثل زیر:

/cmd
	dump.go
	fix.go
	serve.go
	update.go
	web.go

 

قواعد


قوانین کلی

  • تمام برنامه هایی که پکیج main توی خودشون دارن، باید ثابتی داخل پکیج main با اسم APP_VER  تعریف بشه که ورژن فعلی برنامه رو با فرمت X.Y.Z.Date [Status] توی خودش نگه داره. برای مثال: 0.7.6.1112 Beta
const APP_VER = "0.7.6.1112 Beta"
  • تمام کتابخانه ها/پکیج ها باید فانکشنی با اسم Version داشته باشن که ورژن فعلی برنامه رو با فرمت X.Y.Z[.Date] به صورت رشته برگردونه.
  • خط هایی که بیشتر از 80 کاراکتر کد دارند باید به چند خط کد تقسیم بشن، برای توابع واحد تقسیم آرگومان های آن تابع در نظر گرفته میشه.
So(z.ExtractTo(
 	path.Join(os.TempDir(), "testdata/test2"),
 	"dir/", "dir/bar", "readonly"), ShouldBeNil)
  • هنگام تعریف تابع یا متود اگر قسمت تعریف تابع بیشتر از 80 کاراکتر شود باید این مورد رو هم تقسیم کنیم به چند خط، پارامتر های پشت سر هم و هم تایپ باید توی یک خط باشن و یک لاین اضافه باشه که قسمت بدن تابع جدا کنه
// NewNode initializes and returns a new Node representation.
 func NewNode(
 	importPath, downloadUrl string,
 	tp RevisionType, val string,
 	isGetDeps bool) *Node {

 	n := &Node{
 		Pkg: Pkg{
 			ImportPath: importPath,
 			RootPath:   GetRootPath(importPath),
 			Type:       tp,
 			Value:      val,
 		},
 		DownloadURL: downloadUrl,
 		IsGetDeps:   isGetDeps,
 	}
 	n.InstallPath = path.Join(setting.InstallRepoPath, n.RootPath) + n.ValSuffix()
 	return n
 }
  • تعریف گروهی باید بر اساس نوع و کاربرد مقادیر تعریف بشن، نه همه با هم.
 const (
 	// Default section name.
 	DEFAULT_SECTION = "DEFAULT"
 	// Maximum allowed depth when recursively substituing variable names.
 	_DEPTH_VALUES = 200
 )

 type ParseError int

 const (
 	ERR_SECTION_NOT_FOUND ParseError = iota + 1
 	ERR_KEY_NOT_FOUND
 	ERR_BLANK_SECTION_NAME
 	ERR_COULD_NOT_PARSE
 )
  • اگر تمام فانکشن های یک پکیج توی یک فایل تغریف شده باشن، ما باید با استفاده از ASCII Generator یه تایتل برجسته ابتدای فایلمون بزاریم
 // _________                                       __
 // \_   ___ \  ____   _____   _____   ____   _____/  |_
 // /    \  \/ /  _ \ /     \ /     \_/ __ \ /    \   __\
 // \     \___(  <_> )  Y Y  \  Y Y  \  ___/|   |  \  |
 //  \______  /\____/|__|_|  /__|_|  /\___  >___|  /__|
 //         \/             \/      \/     \/     \/
  • فانکشن ها یا متود ها بر اساس وابستگیشون مرتب میشن، به اینصورت که بنیادی ترین فانکشن باید بالاتر از همه بشه، مثلا ExecCmdDirBytes بنیادی ترین فانکشن هست که توسط این ExecCmdDir فاکشن صدا زده میشه و ExecCmdDir هم توسط ExecCmd صدا زده میشه:
 // ExecCmdDirBytes executes system command in given directory
 // and return stdout, stderr in bytes type, along with possible error.
 func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
 	...
 }

 // ExecCmdDir executes system command in given directory
 // and return stdout, stderr in string type, along with possible error.
 func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
 	bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
 	return string(bufOut), string(bufErr), err
 }

 // ExecCmd executes system command
 // and return stdout, stderr in string type, along with possible error.
 func ExecCmd(cmdName string, args ...string) (string, string, error) {
 	return ExecCmdDir("", cmdName, args...)
 }
  • متودها همیشه باید بعد از struct تعریف بشن و بر اساس پر استفاده ترین فیلد struct باید مرتب بشن.
 type Webhook struct { ... }
 func (w *Webhook) GetEvent() { ... }
 func (w *Webhook) SaveEvent() error { ... }
 func (w *Webhook) HasPushEvent() bool { ... }
  • اگر یک struct حاوی فانکشن های عملیاتی بود باید بر اساس CRUD تعریف و مرتب بشن.
 func CreateWebhook(w *Webhook) error { ... }
 func GetWebhookById(hookId int64) (*Webhook, error) { ... }
 func UpdateWebhook(w *Webhook) error { ... }
 func DeleteWebhook(hookId int64) error { ... }
  • اگر یک struct متود یا فانکشن هایی داشت که با Has، Is، Can یا Allow شروع میشن باید باید با ترتیب زیر مرتب بشن:
Has
Is
Can or Allow
  • تعریف متغیر ها باید قبل از تعریف فانکشن یا متود ها انجام بشه.
var CmdDump = cli.Command{
 	Name:  "dump",
 	...
 	Action: runDump,
 	Flags:  []cli.Flag{},
 }

 func runDump(*cli.Context) { ...
  • برای مقداردهی تا جای ممکن از struct و فیلد های نام گذاری شده استفاده بشه.
 AddHookTask(&HookTask{
 	Type:        HTT_WEBHOOK,
 	Url:         w.Url,
 	Payload:     p,
 	ContentType: w.ContentType,
 	IsSsl:       w.IsSsl,
 })

 

 

 

پکیج ها


ایمپورت پکیج

همه مسیرهای ایمپورت پکیج باید یک URL از یک سایت هاستینگ کد(مثل گیتهاب) باشه، به جز پکیج های کتابخانه استاندارد.

بنابراین، چهار نوع پکیج وجود داره که میشه ایمپورت کرد:

  1. پکیج هایی از کتابخانه استاندارد
  2. پکیج هایی شخص ثالث
  3. پکیج هایی که در همان سازمان هستند اما در یک مخزن نیستند
  4. پکیج هایی که در همان مخزن

قوانین

  • با استفاده از یک خط خالی پکیج های متفاوت باید جدا و گروه بندی بشن.
  • نباید از یک مسیر نسبی ( ./subpackage) به عنوان مسیر ایمپورت استفاده کرد، همه مسیرهای ایمپورت باید go get-able باشند.
import (
    "fmt"
    "html/template"
    "net/http"
    "os"

    "github.com/codegangsta/cli"
    "gopkg.in/macaron.v1"

    "github.com/gogs/git"
    "github.com/gogs/gfm"

    "github.com/gogs/gogs/routes"
    "github.com/gogs/gogs/routes/repo"
    "github.com/gogs/gogs/routes/user"
)