Method Sets

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

  1. Value Receivers: Don't modify the receiver, work with copies
  2. Pointer Receivers: Can modify the receiver's state
  3. Method Chaining: Return *Type from pointer methods to enable chaining
  4. Type Methods: Can define methods on any named type
  5. Encapsulation: Use unexported fields with method access