I. Proxy Pattern

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. This can be useful in scenarios where the real object is resource-intensive to create or manipulate, and the proxy object can perform additional tasks such as caching, logging, or security checks. In Ruby, the Proxy pattern can be implemented using a proxy class that delegates method calls to the real object.

Here’s an example of a simple proxy class in Ruby:

class BankAccount
  attr_reader :balance

  def initialize(balance)
    @balance = balance
  end

  def deposit(amount)
    @balance += amount
  end

  def withdraw(amount)
    @balance -= amount
  end
end

class BankAccountProxy
  def initialize(real_account)
    @real_account = real_account
  end

  def balance
    @real_account.balance
  end

  def deposit(amount)
    @real_account.deposit(amount)
  end

  def withdraw(amount)
    @real_account.withdraw(amount)
  end
end

# Usage
real_account = BankAccount.new(100)
proxy = BankAccountProxy.new(real_account)

puts "Balance: #{proxy.balance}"
proxy.deposit(50)
puts "Balance: #{proxy.balance}"
proxy.withdraw(30)
puts "Balance: #{proxy.balance}"

In this example, the BankAccountProxy class acts as a proxy for the BankAccount class, delegating method calls to the real account object. The proxy object can perform additional tasks before or after delegating the method calls, such as logging or security checks.

II. Composite Pattern

The Composite pattern allows you to compose objects into tree structures to represent part-whole hierarchies. This pattern is useful when you want clients to treat individual objects and compositions of objects uniformly. In Ruby, the Composite pattern can be implemented using a common interface for both leaf and composite objects.

Here’s an example of a simple composite pattern in Ruby:

class Component
  def operation
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

class Leaf < Component
  def operation
    puts "Leaf operation"
  end
end

class Composite < Component
  def initialize
    @children = []
  end

  def add(child)
    @children << child
  end

  def remove(child)
    @children.delete(child)
  end

  def operation
    puts "Composite operation"
    @children.each { |child| child.operation }
  end
end

# Usage

leaf1 = Leaf.new
leaf2 = Leaf.new
composite = Composite.new

composite.add(leaf1)
composite.add(leaf2)

composite.operation

III. State Pattern

The State pattern allows an object to alter its behavior when its internal state changes. This pattern is useful when an object’s behavior depends on its state and needs to change dynamically at runtime. In Ruby, the State pattern can be implemented using a state interface and concrete state classes that encapsulate the object’s behavior.

Here’s an example of the State pattern in Ruby:

class Context
  attr_writer :state

  def request
    @state.handle
  end
end

class State
  def handle
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

class ConcreteStateA < State
  def handle
    puts "Handling request in state A"
  end
end

class ConcreteStateB < State
  def handle
    puts "Handling request in state B"
  end
end

# Usage

context = Context.new
context.state = ConcreteStateA.new
context.request

context.state = ConcreteStateB.new
context.request

In this example, the Context class maintains a reference to a State object, which encapsulates the object’s behavior. The ConcreteStateA and ConcreteStateB classes implement the state interface and define the behavior for the object in different states.

IV. Chain of Responsibility Pattern

The Chain of Responsibility pattern allows you to chain multiple handlers to process a request. Each handler in the chain has the ability to process the request or pass it on to the next handler in the chain. This pattern is useful when you want to decouple the sender of a request from its receivers and allow multiple objects to handle the request.

Here’s an example of the Chain of Responsibility pattern in Ruby:

class Handler
  attr_writer :successor

  def handle_request(request)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

class ConcreteHandlerA < Handler
  def handle_request(request)
    if request == 'A'
      puts "Handling request A"
    elsif @successor
      @successor.handle_request(request)
    end
  end
end

class ConcreteHandlerB < Handler
  def handle_request(request)
    if request == 'B'
      puts "Handling request B"
    elsif @successor
      @successor.handle_request(request)
    end
  end
end

# Usage

handler_a = ConcreteHandlerA.new
handler_b = ConcreteHandlerB.new

handler_a.successor = handler_b

handler_a.handle_request('A')
handler_a.handle_request('B')
handler_a.handle_request('C')

In this example, the Handler class defines the interface for handling requests, and concrete handler classes (ConcreteHandlerA and ConcreteHandlerB) implement the request handling logic. The handlers are chained together, and each handler decides whether to handle the request or pass it on to the next handler in the chain.

V. Iterator Pattern

The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. This pattern is useful when you want to iterate over a collection of objects without knowing the internal structure of the collection. In Ruby, the Iterator pattern can be implemented using an iterator interface and concrete iterator classes for different types of collections.

Here’s an example of the Iterator pattern in Ruby:

class Iterator
  def has_next?
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  def next
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

class ArrayIterator < Iterator
  def initialize(array)
    @array = array
    @index = 0
  end

  def has_next?
    @index < @array.length
  end

  def next
    value = @array[@index]
    @index += 1
    value
  end
end

# Usage

array = [1, 2, 3, 4, 5]

iterator = ArrayIterator.new(array)

while iterator.has_next?
  puts iterator.next
end

In this example, the Iterator class defines the interface for iterating over a collection, and the ArrayIterator class implements the iterator interface for arrays. The iterator object can traverse the elements of the array without exposing the internal structure of the array.