গোল্যাঙ্গ কোডিং মান মৌলিক নির্দেশিকা

সম্পদ মুক্ত করার জন্য defer ব্যবহার করুন

ফাইল এবং লক প্রতিরোধ সহ সম্পদ মুক্ত করার জন্য defer ব্যবহার করুন।

প্রশ্নজনক:

p.Lock()
if p.count < 10 {
  p.Unlock()
  return p.count
}

p.count++
newCount := p.count
p.Unlock()

return newCount

// এই প্রতিরোধের বিভিন্ন অংশগুলির জন্য মনে রাখা সহজ।

প্রস্তাবিত:

p.Lock()
defer p.Unlock()

if p.count < 10 {
  return p.count
}

p.count++
return p.count

// আরো পঠিত

defer-এর অতিরিক্ত অবিস্মরণী খরচ খুব কম, তাই এটি নানোসেকেন্ড স্তরে যখন ফাংশন সময় পূর্বাভাস করা যায় তখন মাত্র এটি অবলম্বন করা উচিত। ব্যবহার এর খরচ অপেক্ষাকৃত স্বাভাবিক। এটি বিশেষত যারা ছোট নয়, বরং শুধুমাত্র সাধারণ মেমোরি অ্যাক্সেস ছাড়া অন্যান্য গণনার সম্পদ ব্যবহারের খরচ পরিপূরণ করে।

চ্যানেল আকার পরামর্শিত 1 বা বাফারহীন হওয়া উচিত

চ্যানেলের আকার সাধারণত 1 অথবা বাফারহীন হবে। ডিফল্ট ভাবে, চ্যানেল বাফারহীন হওয়া সহ আকার ০ হল। অন্য আকার ব্যাখ্যামূলকভাবে পর্যালোচনা করা উচিত। আমাদের একটি চ্যানেলের আকার কীভাবে নির্ধারণ হবে, চ্যানেল লিখতে গেলে উচিত কী বিরতি চ্যানেল লেখা না হয় এবং এটি ঘটার সময় সিস্টেমের যে পরিবর্তন ঘটায় এটি পরিবেশনা করা উচিত।

প্রশ্নজনক:

// অনুমান করা হউক, এইটা যে কোন অবস্থার সাথে মোকাবিলা করার জন্য প্রয়োজনীয়!
c := make(chan int, 64)

প্রস্তাবিত:

// আকার: 1
c := make(chan int, 1) // বা
// বাফারহীন চ্যানেল, আকার ০
c := make(chan int)

এনাম শুরু হয় 1 থেকে

Go-তে এনাম বৃহৎ ধারণার সাথে একটি কাস্টম ধরন ও একটি কন্স্ট গ্রুপ ঘোষণা করা ওইওটা ব্যবহার করা। শুধুমাত্র প্রথম মানটি হলো 0, এনাম সাধারণভাবে অবশ্যই শূন্যমুক্ত হবে।

প্রশ্নজনক:

type Operation int

const (
  Add Operation = iota
  Subtract
  Multiply
)

// Add=0, Subtract=1, Multiply=2

প্রস্তাবিত:

type Operation int

const (
  Add Operation = iota + 1
  Subtract
  Multiply
)

// Add=1, Subtract=2, Multiply=3

কিছু মুল্যবিশেষতা ব্যবহার করা সময় শূন্যমুক্ত থেকে এনাম শুরু করা (শূন্য থেকে শুরু হয়) এমন কিছু অবস্তানে সম্ভব। উদাহরণস্বরূপ, যখন শূন্যমুক্ত মানটি আনুষঙ্গিকভাবে সুনির্দিষ্ট আচরণের মূল্য।

type LogOutput int

const (
  LogToStdout LogOutput = iota
  LogToFile
  LogToRemote
)

// LogToStdout=0, LogToFile=1, LogToRemote=2

এটমিক ব্যবহার করুন

মৌলিক প্রকার (int32, int64, ইত্যাদি) -এর উপর অটোমিক অপারেশন ব্যবহার করুন sync/atomic প্যাকেজ থেকে, কারণ স্পষ্ট করা সহ প্রয়োজন মতো ভুল হয়ে পড়ে এই অপারেশন গুলি পড়ে।

go.uber.org/atomic এই অপারেশনগুলির জন্য ধরন নিরাপত্তা যুক্ত করে দেয় এবং অতিথনতম অবস্থানসপঞ্জন্য atomic.Bool ধরন যুক্ত করে দেয়।

প্রশ্নজনক পদ্ধতি:

type foo struct {
  running int32  // atomic
}

func (f* foo) start() {
  if atomic.SwapInt32(&f.running, 1) == 1 {
     // ইতিমধ্যে বুঝলে…
     ফিরে যৌক্তিক
  }
  // Foo শুরু করুন
}

func (f *foo) isRunning() bool {
  return f.running == 1  // চ্যাপের!
}

প্রস্তাবিত পদ্ধতি:

type foo struct {
  running atomic.Bool
}

func (f *foo) start() {
  if f.running.Swap(true) {
     // ইতিমধ্যে বুঝলে…
     ফিরে যৌক্তিক
  }
  // Foo শুরু করুন
}

func (f *foo) isRunning() bool {
  return f.running.Load()
}

মুলতুত বদলা করা ভূত্বক গ্লোবাল ভ্যারিয়েবল থেকে বিরত থাকুন

ব্যাপার গুলি সংশ্লিষ্ট ফাংশন পয়েন্টার ও অন্যান্য মানের ধরনগুলির জন্য এই নিয়ম প্রয়োগ করুন।

অনুমোদিত পদ্ধতি 1:

// sign.go
var _timeNow = time.Now
func sign(msg string) string {
  now := _timeNow()
  return signWithTime(msg, now)
}

সুপারিশ করা হচ্ছে 1:

// sign.go
type signer struct {
  now func() time.Time
}
func newSigner() *signer {
  return &signer{
    now: time.Now,
  }
}
func (s *signer) Sign(msg string) string {
  now := s.now()
  return signWithTime(msg, now)
}

অনুমোদিত পদ্ধতি 2:

// sign_test.go
func TestSign(t *testing.T) {
  oldTimeNow := _timeNow
  _timeNow = func() time.Time {
    return someFixedTime
  }
  defer func() { _timeNow = oldTimeNow }()
  assert.Equal(t, want, sign(give))
}

সুপারিশ করা হচ্ছে 2:

// sign_test.go
func TestSigner(t *testing.T) {
  s := newSigner()
  s.now = func() time.Time {
    return someFixedTime
  }
  assert.Equal(t, want, s.Sign(give))
}

পূর্বানুলিপ্ত শনাক্তগুলি ব্যবহার করা থেকে বিরত থাকুন

Go Language Specification এ গো প্রজেক্টে ব্যবহৃত হওয়া বিভিন্ন পূর্বানুলিপ্ত শনাক্তগুলি বিবেচনা করা হয়েছে।

প্রস্তাবিত অনুমোদিত প্রথা 1:

var error string
// `error` implicit-ভাবে প্রধান অভিধানটির ঘোষণা করে

// অথবা

func handleErrorMessage(error string) {
    // `error` implicit-ভাবে প্রধান অভিধানটির ঘোষণা করে
}

সুপারিশ করা হচ্ছে 1:

var errorMessage string
// `error` এখন নিষ্পাদিত প্রধান অভিধানটির মাধ্যমে পয়েন্ট করে

// অথবা

func handleErrorMessage(msg string) {
    // `error` এখন নিষ্পাদিত প্রধান অভিধানটির মাধ্যমে পয়েন্ট করে
}

প্রস্তাবিত অনুমোদিত প্রথা 2:

type Foo struct {
    // যেহেতু এই ফিল্ডগুলি ধারা করে বিষয়গুলি নাকি ছাড়া যায়, `error` বা `string` strings পুনরায় ঘোষণা করা এখন অস্পষ্ট হয়েছে।
    error  error
    string string
}

func (f Foo) Error() error {
    // `error` এবং `f.error` দৃশ্যপূর্ণভাবে অনুপ্রেরণ করে
    return f.error
}

func (f Foo) String() string {
    // `string` এবং `f.string` দৃশ্যপূর্ণভাবে অনুপ্রেরণ করে
    return f.string
}

সুপারিশ করা হচ্ছে 2:

type Foo struct {
    // `error` এবং `string` এখন ব্যাখ্যামূলক ভাবে স্পষ্টভাবে ঘোষণা করা হয়েছে।
    err error
    str string
}

func (f Foo) Error() error {
    return f.err
}

func (f Foo) String() string {
    return f.str
}

মনে রাখা দরকার যে, প্রিকম্পাইলার ব্যবহার করলে, পূর্বাপন্ন শনাক্তগুলি ব্যবহার করে এরর সৃষ্টি করে না, তবে go vet এর মত টুল ঠিকমতো এই এবং অন্যান্য বৈশিষ্ট্যিক সমস্যা নির্দেশ করবে।

এই init() ব্যবহার করা এড়ানো যেতে পারে

init() ব্যবহার করা এড়ানো যেতে পারে। যদি init() অবজেক্টের জন্য সবচেয়ে ভালো সম্পূর্ণতা নির্ভর করে অথবা পছন্দসই, তবে কোডটি নিম্নলিখিত মানদণ্ড মেনে চলতে হবেঃ

১। প্রোগ্রাম পরিবেশ অথবা কলের বিচ্ছিন্নতা বা পুরোনোতা উল্লেখপূর্বক সম্পূর্ণতা নিশ্চিত করা। ২। অন্যান্য init() ফাংশনগুলির আদেশ বা পার্শ্বপ্রভাব থেকে বিরত থাকা। init() এর আদেশ প্রকাশিত হলেও, কোড পরিবর্তন হতে পারে, তাই init() ফাংশনের মধ্যের সম্পর্কটিপূর্ণ বানানের কারণে কোডটি ভঙ্গীস্বরূপ এবং ভুলো গভীরভাবে সমাধান করা যাবে। ৩। মেশিনের তথ্য, পরিবেশের ভ্যারিয়েবল, কার্যরত ডিরেক্টরি, প্রোগ্রাম প্যারামিটার / ইনপুট ইত্যাদি সহ গ্লোবাল বা পরিবেশাত্মক অবস্থার অ্যাক্সেস বা পরিবর্তন থেকে বিরত থাকা। ৪। ফাইল সিস্টেম, নেটওয়ার্কিং এবং সিস্টেম কল সহ আই/ও থেকে বিরত থাকা।

এই সেরা গুনাবলা লেখার প্রয়োজনীয় না হলে main() কলের (অথবা প্রোগ্রামের জীবনকলা অন্য নাথান) অংশ হিসাবে থাকতে পারে বা সম্পূর্ণতা তর঍ ব্যবহার করা হতে পারে। বিশেষভাবে, অন্যান্য প্রোগ্রামগুলি ব্যবহার করার জন্য উপযুক্ত গ্রন্থাগারগুলি পরিপূর্ণতা বিচার করা উচিত, "init জাদু" করার বিপরীতে।

Unrecommended approach 1:

type Foo struct {
    // ...
}
var _defaultFoo Foo
func init() {
    _defaultFoo = Foo{
        // ...
    }
}

Recommended approach 1:

var _defaultFoo = Foo{
    // ...
}
// or, for better testability:
var _defaultFoo = defaultFoo()
func defaultFoo() Foo {
    return Foo{
        // ...
    }
}

Unrecommended approach 2:

type Config struct {
    // ...
}
var _config Config
func init() {
    // খারাপ: বর্তমান ডিরেক্টরির উপর ভিত্তি করে
    cwd, _ := os.Getwd()
    // খারাপ: আই/ও
    raw, _ := os.ReadFile(
        path.Join(cwd, "config", "config.yaml"),
    )
    yaml.Unmarshal(raw, &_config)
}

Recommended approach 2:

type Config struct {
    // ...
}
func loadConfig() Config {
    cwd, err := os.Getwd()
    // ত্রুটি হ্যান্ডেল
    raw, err := os.ReadFile(
        path.Join(cwd, "config", "config.yaml"),
    )
    // ত্রুটি হ্যান্ডেল
    var config Config
    yaml.Unmarshal(raw, &config)
    return config
}

উপরোক্ত বিবেচনার মধ্যে, কিছু ক্ষেত্রে, init() বেশি পছন্দিত অথবা প্রয়োজনীয় হতে পারে, যেমনঃ

  • একটি জটিল অভিব্যাক্তির একক প্রতিস্থাপন হিসাবে প্রতিষ্ঠিত না করা যায়।
  • সন্নিবিষ্ট হুক, উদাহরণস্বরূপ database/sql, ধরণ নিবন্ধন ইত্যাদি।