Exercise: Method Sets
Difficulty - Beginner
Learning Objectives
- Understand method definitions and receivers
- Learn the difference between pointer and value receivers
- Practice method chaining
- Understand method sets and interface satisfaction
- Master type methods
Problem Statement
Create types with methods that demonstrate different receiver types and method patterns.
Type Definitions
1package methodsets
2
3import "fmt"
4
5// Rectangle represents a rectangle shape
6type Rectangle struct {
7 Width float64
8 Height float64
9}
10
11// NewRectangle creates a new rectangle
12func NewRectangle(width, height float64) *Rectangle
13
14// Area returns the area of the rectangle
15func Area() float64
16
17// Perimeter returns the perimeter
18func Perimeter() float64
19
20// Scale scales the rectangle by a factor
21func Scale(factor float64) *Rectangle
22
23// String returns a string representation
24func String() string
25
26// BankAccount represents a simple bank account
27type BankAccount struct {
28 balance float64
29 owner string
30}
31
32// NewBankAccount creates a new bank account
33func NewBankAccount(owner string, initialBalance float64) *BankAccount
34
35// Deposit adds money to the account
36func Deposit(amount float64) error
37
38// Withdraw removes money from the account
39func Withdraw(amount float64) error
40
41// Balance returns the current balance
42func Balance() float64
43
44// Transfer transfers money to another account
45func Transfer(to *BankAccount, amount float64) error
46
47// Temperature represents a temperature value
48type Temperature float64
49
50// Celsius returns temperature in Celsius
51func Celsius() float64
52
53// Fahrenheit returns temperature in Fahrenheit
54func Fahrenheit() float64
55
56// Kelvin returns temperature in Kelvin
57func Kelvin() float64
Example Usage
1package main
2
3import (
4 "fmt"
5 "methodsets"
6)
7
8func main() {
9 // Rectangle with method chaining
10 rect := methodsets.NewRectangle(10, 5)
11 fmt.Printf("Original: %v\n", rect)
12 fmt.Printf("Area: %.2f\n", rect.Area())
13
14 rect.Scale(2).Scale(1.5)
15 fmt.Printf("After scaling: %v\n", rect)
16 fmt.Printf("New Area: %.2f\n", rect.Area())
17
18 // Bank account
19 alice := methodsets.NewBankAccount("Alice", 1000)
20 bob := methodsets.NewBankAccount("Bob", 500)
21
22 alice.Deposit(200)
23 alice.Withdraw(100)
24 fmt.Printf("Alice balance: $%.2f\n", alice.Balance())
25
26 alice.Transfer(bob, 300)
27 fmt.Printf("After transfer - Alice: $%.2f, Bob: $%.2f\n",
28 alice.Balance(), bob.Balance())
29
30 // Temperature conversions
31 temp := methodsets.Temperature(25) // 25°C
32 fmt.Printf("%.2f°C = %.2f°F = %.2fK\n",
33 temp.Celsius(), temp.Fahrenheit(), temp.Kelvin())
34}
Solution
Click to see the complete solution
1package methodsets
2
3import (
4 "fmt"
5)
6
7// Rectangle represents a rectangle shape
8type Rectangle struct {
9 Width float64
10 Height float64
11}
12
13// NewRectangle creates a new rectangle
14func NewRectangle(width, height float64) *Rectangle {
15 return &Rectangle{Width: width, Height: height}
16}
17
18// Area returns the area of the rectangle
19func Area() float64 {
20 return r.Width * r.Height
21}
22
23// Perimeter returns the perimeter
24func Perimeter() float64 {
25 return 2 *
26}
27
28// Scale scales the rectangle by a factor
29func Scale(factor float64) *Rectangle {
30 r.Width *= factor
31 r.Height *= factor
32 return r // Enable chaining
33}
34
35// String returns a string representation
36func String() string {
37 return fmt.Sprintf("Rectangle(%.2f x %.2f)", r.Width, r.Height)
38}
39
40// BankAccount represents a simple bank account
41type BankAccount struct {
42 balance float64
43 owner string
44}
45
46// NewBankAccount creates a new bank account
47func NewBankAccount(owner string, initialBalance float64) *BankAccount {
48 return &BankAccount{
49 owner: owner,
50 balance: initialBalance,
51 }
52}
53
54// Deposit adds money to the account
55func Deposit(amount float64) error {
56 if amount <= 0 {
57 return fmt.Errorf("deposit amount must be positive")
58 }
59 b.balance += amount
60 return nil
61}
62
63// Withdraw removes money from the account
64func Withdraw(amount float64) error {
65 if amount <= 0 {
66 return fmt.Errorf("withdrawal amount must be positive")
67 }
68 if amount > b.balance {
69 return fmt.Errorf("insufficient funds")
70 }
71 b.balance -= amount
72 return nil
73}
74
75// Balance returns the current balance
76func Balance() float64 {
77 return b.balance
78}
79
80// Transfer transfers money to another account
81func Transfer(to *BankAccount, amount float64) error {
82 if err := b.Withdraw(amount); err != nil {
83 return err
84 }
85 if err := to.Deposit(amount); err != nil {
86 // Rollback the withdrawal
87 b.Deposit(amount)
88 return err
89 }
90 return nil
91}
92
93// Temperature represents a temperature value
94type Temperature float64
95
96// Celsius returns temperature in Celsius
97func Celsius() float64 {
98 return float64(t)
99}
100
101// Fahrenheit returns temperature in Fahrenheit
102func Fahrenheit() float64 {
103 return float64(t)*9/5 + 32
104}
105
106// Kelvin returns temperature in Kelvin
107func Kelvin() float64 {
108 return float64(t) + 273.15
109}
Key Takeaways
- Value Receivers: Don't modify the receiver, work with copies
- Pointer Receivers: Can modify the receiver's state
- Method Chaining: Return
*Typefrom pointer methods to enable chaining - Type Methods: Can define methods on any named type
- Encapsulation: Use unexported fields with method access
Related Topics
- Structs and Methods - Main tutorial
- Pointers - Pointer receivers
- Interfaces - Method sets and interfaces