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.
Public comments are closed, but I love hearing from readers. Feel free to contact me with your thoughts.