متدها در زبان GoLang


با سلام

زبان گولنگ یک زبان OOP نیست و مفهومی با عنوان کلاس(class) که در این زبان ها هست رو نداره اما میتوانیم برای انواع داده (type) متد تعریف کنیم.

متد در زبان گو شباهت زیادی به فانکشن دارد، در واقع یک متد یک فانکشن با آرگومان receiver خاص هست.

خب بهتره جهت یادآوری و هم درک بهتر موضوع رجوعی به بحث فانکش ها داشته باشیم.

func avg(x, y float64) float64 {
        return (x + y) / 2
}

یک فانکشن میتواند هیچ یا چند آرگومان داشته باشد و توجه داشته باشید که بعد از تعریف آرگومان ها می توانیم نوع خروجی رو رو هم مشخص می کنیم(اختیاری است)
نوع آرگومان بعد از اسم آنها می‌آید و اگر مانند مثال بالا نوع همه آرگومان ها یکسان باشد می توانیم نوع را فقط برای آخرین مورد تعریف کنیم.
فانکش ها در زبان گولنگ خصوصیات دیگری مانند مقدار خروجی نام گذاری شده و چند خروجی را دارند که با توجه به موضوع اصلی مقاله نیاز به بررسی این موارد رو نمی بینم.


خب برگردیم به سراغ متد ها

نوع receiver که در بالا اشاره کردیم بین کلمه func و نام تابع(MethodName) قرار میگیرد. ریسیور یک نام و نوع دارد:

func (receiver Type) MethodName(parameterList) (returnTypes) { 
}


در مثال زیر متد show یک receiver از نوعauthor با نام a میگیرد و کارش این است که اطلاعات یک نویسنده رو نمایش دهند.

package main
import &quotfmt&quot
// Author structure
type author struct {
    name      string
    branch    string
   particles int
   salary    int
}
// Method with a receive  of author type
func (a author) show() {
    fmt.Println(&quotAuthor's Name: &quot, a.name)
    fmt.Println(&quotBranch Name: &quot, a.branch)
    fmt.Println(&quotPublished articles: &quot, a.particles)
    fmt.Println(&quotSalary: &quot, a.salary)
}
// Main function
func main() {
    // Initializing the values of the author structure
res := author{
    name:      &quotSona&quot,
    branch:    &quotCSE&quot,
    particles: 203,
   salary:    34000,
 }
 // Calling the method
 res.show()
}


توجه کنید که author از نوع struct می باشد. ما می توانیم برای انواع Non-Struct هم متد تعریف کنیم، در مثال زیر ما برای نوع سفارشی MyString که ساخته ایم یک متد به نام reverse برای معکوس کردن رشته مقادیر آن اضافه کردیم که به راحتی می توانیم آن را صدا بزنیم.


package main

import (
	&quotfmt&quot
)

type MyString string

func (myStr MyString) reverse() string {
	s := string(myStr)
	runes := []rune(s)

	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}
	return string(runes)
}

func main() {
	myStr := MyString(&quotOLLEH&quot)
	fmt.Println(myStr.reverse())
}


شما میتوانید متدهایی با ورودی پوینتر(Pointer) تعریف کنید:

// Method with pointer receiver
func (receiver *Type) MethodName(parameterList) (returnTypes) {
 // code
}


با استفاده از آرگومان از نوع اشاره گر می توانید مقادیر receiver را تغییر دهید. در حالی که در حالت قبلی(value receiver) این امکان پذیر نبود و صرفا یک کپی از receiver به متد پاس داده می شد.

به طور مثال اگر بخواهیم برای نوع author مثال قبلی یک متد برای تغییر ویژگی branch آن اضافه کنیم، بدین صورت عمل می کنیم:

// Method with a receiver of author type
func (a *author) changeBranch(branch string) {
      (*a).branch = branch
}


بر خلاف فانکشن ها، متدها می توانند هم آرگومان اشاره گر(Pointer) و هم مقدار(Value)را بپذیرند:

همانطور که می دانیم در Go هنگامی که یک تابع دارای آرگومان مقدار(value) است، فقط مقادیری که به صورت value هستند را به عنوان پارامتر را می پذیرد و اگر بخواهید یک Pointer را به یک تابع Value منتقل کنید( یا برعکس)، آن را نمی پذیرد. اما یک متد Go می تواند مقادیر(Value) و اشاره گر(Pointer) را بپذیرد. همانطور که در مثال زیر نشان داده شده است:


package main
import &quotfmt&quot

// Author structure
type author struct {
	name string
	branch string
}

// Method with a pointer receiver of author type
func (a *author) show_1(abranch string) {
	(*a).branch = abranch
}

// Method with a value  receiver of author type
func (a author) show_2() {
	a.name = &quotGourav&quot
	fmt.Println(&quotAuthor's name(Before) : &quot, a.name)
}

// Main function
func main() {
      // Initializing the values of the author structure
	res := author{
             name: &quotSona&quot,
            branch: &quotCSE&quot,
       }
       fmt.Println(&quotBranch Name(Before): &quot, res.branch)
       // Calling the show_1 method (pointer method) with value
       res.show_1(&quotECE&quot)
      fmt.Println(&quotBranch Name(After): &quot, res.branch)
  
       // Calling the show_2 method (value method) with a pointer
       (&res).show_2()
      fmt.Println(&quotAuthor's name(After): &quot, res.name)
}


خروجی:

Branch Name(Before):  CSE
Branch Name(After):  ECE
Author's name(Before) :  Gourav
Author's name(After):  Sona

در مثال فوق می بینید که چطور آرگومان های دلخواه (از نوع value یا pointer) به متدها پاس داده می شوند.

چرا از متدها به جای فانکشن ها استفاده کنیم ؟

۱- متد ها به شما اجازه می دهند تا با استایل OOP در گولنگ کد بزنید، گو یک زبان برنامه نویسی شی گرا نیست و از کلاس ها پشتیبانی نمی کند. از این رو تعریف متدها بر روی typeها راهی برای دستیابی به رفتاری مشابه کلاس ها هستند. متدها به یک گروه بندی منطقی از رفتار مربوط به type شبیه کلاس ها اجازه می دهند.

۲- متد کال ها در خوانایی و درک آسان کد بهتر هستند، متدهای مختلف با نام یکسان را می توان در انواع مختلف type تعریف کرد در حالی که توابع هم نام مجاز نیستند. یک مثال ساده فرض کنیم که ما یک ساختار(struct) مربع(Square) و دایره(Circle) داریم. می توان متدهای هم نام Area را در مربع و دایره تعریف کرد.
۳- علاوه بر این متد ها باعث جلوگیری از کانفلیکت نام گذاری می شوند. چون متد به یک نوع(type) وابسته شده، شما می توانید از همان متد بر روی نوع های مختلف داشته باشید

۴- متد میتونه مقدار ریسیور رو که پوینتر بهش اشاره میکنه تغییر بدهد، اینطوری از کپی کردن مقدار(value) در هر فراخوانی خودداری میشوذ. اگر receiver یک استراکت بزرگ باشد ، این می تواند کارآمدتر(efficient) باشد.

خب در این مقاله با متد ها و نحوه نوشتن آنها در گپلنگ آشنا شدیم و همینطور یادگرفتیم که متدها چه فرق هایی با فانکشن ها دارند.


سخن پایانی: لطفا نظرات و انتقادات سازنده خودتون رو دریغ نکنید من حتما استقبال میکنم.