متد تابعی است که یک آرگومان خاص به اسم «گیرنده» دارد.
گیرنده در فهرست آرگومانهای خودش، بین کلیدواژهی func و اسم متد ظاهر میشود.
func (receiver type) MethodName(parameters) (returnTypes) {
}
شما فقط میتوانید متدی را با گیرندهای تعریف کنید که نوعش در همان «پکیجی» تعریف شده باشد که متد در آن قرار دارد.
package person
type Person struct {
Name string
}
func (p Person) Greetings() string {
return fmt.Sprintf("Welcome %s!", p.Name)
}
متدی که روی ساختار تعریف شده را میتوان با نشانهگذاری نقطهای فراخوانی کرد.
p := Person{Name: "Bronson"}
fmt.Println(p.Greetings())
// Output: Welcome Bronson!
به نحوهی فراخوانی متد Greetings() روی نمونهی Person به اسم p توجه کنید.
این دقیقاً مثل همان روشی است که در یک زبان برنامهنویسی شیءگرا متدها را فراخوانی میکنید.
یادتان باشد: متد فقط یک تابع با آرگومان گیرنده است. متدها به جلوگیری از تضاد نامها کمک میکنند. چون متد به نوع گیرندهی مشخصی گره خورده است، میتوانید اسم متد یکسانی روی نوعهای مختلف داشته باشید.
import "math"
type rect struct {
width, height int
}
func (r rect) area() int {
return r.width * r.height
}
type circle struct {
radius int
}
func (c circle) area() float64 {
return math.Pow(float64(c.radius), 2) * math.Pi
}
دو نوع گیرنده وجود دارد: گیرندهی «مقداری» و گیرندهی «اشارهگر».
همهی متدهایی که تا اینجا دیدهایم گیرندهی مقداری دارند، یعنی کپیای از مقداری را دریافت میکنند که به متد پاس داده شده است. به این معنا که هر تغییری که داخل متد روی گیرنده انجام شود، برای فراخواننده قابل مشاهده نیست.
میتوانید متدهایی با گیرندهی اشارهگر تعریف کنید تا مقداری را که گیرنده به آن اشاره میکند تغییر دهید.
این کار با گذاشتن یک * پیش از اسم نوع انجام میشود.
برای مثال برای نوع rect، گیرندهی اشارهگر بهصورت *rect تعریف میشود.
چنین تغییراتی برای فراخوانندهی متد هم قابل مشاهده هستند.
type rect struct {
width, height int
}
func (r *rect) squareIt() {
r.height = r.width
}
r := rect{width: 10, height: 20}
fmt.Printf("Width: %d, Height: %d\n", r.width, r.height)
// Output: Width: 10, Height: 20
r.squareIt()
fmt.Printf("Width: %d, Height: %d\n", r.width, r.height)
// Output: Width: 10, Height: 10
توجه: این تمرین ادامهی تمرین need-for-speed است.
در این تمرین، شما مسابقههایی بین انواع مختلف ماشینهای کنترلی ترتیب میدهید. هر ماشین سرعت و ویژگیهای مصرف باتری خودش را دارد.
ماشینها با باتری کاملاً پر (۱۰۰٪) شروع میکنند. هر بار که ماشین را با کنترل از راه دور میرانید، به اندازهی سرعت ماشین متر جلو میرود و به اندازهی مصرف باتریاش از درصد باتری باقیمانده کم میشود.
اگر باتری ماشین کمتر از درصد مصرف باتری آن باشد، دیگر نمیتوانید ماشین را برانید.
ماشین کنترلی یک نمایشگر LED شیک دارد که دو مورد اطلاعات را نشان میدهد:
"Driven <METERS> meters"."Battery at <PERCENTAGE>%".هر پیست مسابقه مسافت خودش را دارد. ماشینها با بررسی اینکه آیا میتوانند پیست را بدون تمام شدن باتری به پایان برسانند، آزمایش میشوند.
متد Drive را روی Car پیادهسازی کنید که تعداد مترهای طیشده را بر اساس سرعت ماشین بهروزرسانی میکند و باتری را بر اساس مصرف باتری کاهش میدهد:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
car.Drive()
// car is now Car{speed: 5, batteryDrain: 2, battery: 98, distance: 5}
توجه: اگر انجام این کار باعث شود باتری ماشین زیر ۰ برود، نباید سعی کنید ماشین را برانید.
یک متد DisplayDistance روی Car پیادهسازی کنید تا مسافت را همانطور که روی نمایشگر LED نمایش داده میشود، بهصورت یک string برگرداند:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
fmt.Println(car.DisplayDistance())
// Output: "Driven 0 meters"
متد DisplayBattery را روی Car پیادهسازی کنید تا درصد باتری را همانطور که روی نمایشگر LED نمایش داده میشود، بهصورت یک string برگرداند:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
fmt.Println(car.DisplayBattery())
// Output: "Battery at 100%"
برای تمام کردن یک مسابقه، ماشین باید بتواند مسافت مسابقه را طی کند.
یعنی نباید باتریاش پیش از عبور از خط پایان تمام شود.
متد CanFinish را پیادهسازی کنید که یک trackDistance int را به عنوان پارامتر میگیرد و اگر ماشین بتواند مسابقه را تمام کند true و در غیر این صورت false را برمیگرداند:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
trackDistance := 100
car.CanFinish(trackDistance)
// => true