Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Ruby Programming
Previous Page Home Next Page

The Visitor Pattern

It's the method each.

Library: delegate

Object delegation is a way of composing objects---extending an object with the capabilities of another---at runtime. This promotes writing flexible, decoupled code, as there are no compile-time dependencies between the users of the overall class and the delegates.

The Ruby Delegator class implements a simple but powerful delegation scheme, where requests are automatically forwarded from a master class to delegates or their ancestors, and where the delegate can be changed at runtime with a single method call.

The delegate.rb library provides two mechanisms for allowing an object to forward messages to a delegate.

  1. For simple cases where the class of the delegate is fixed, arrange for the master class to be a subclass of DelegateClass, passing the name of the class to be delegated as a parameter (Example 1). Then, in your class's initialize method, you'd call the superclass, passing in the object to be delegated. For example, to declare a class Fred that also supports all the methods in Flintstone, you'd write

    class Fred < DelegateClass(Flintstone)
      def initialize
        # ...
        super(Flintstone.new(...))
      end
      # ...
     end
    
    This is subtly different from using subclassing. With subclassing, there is only one object, which has the methods and the defined class, its parent, and their ancestors. With delegation there are two objects, linked so that calls to one may be delegated to the other.

  2. For cases where the delegate needs to be dynamic, make the master class a subclass of SimpleDelegator (Example 2). You can also add delegation capabilities to an existing object using SimpleDelegator (Example 3). In these cases, you can call the __setobj__ method in SimpleDelegator to change the object being delegated at runtime.

Example 1. Use the DelegateClass method and subclass the result when you need a class with its own behavior that also delegates to an object of another class. In this example, we assume that the @sizeInInches array is large, so we want only one copy of it. We then define a class that accesses it, converting the values to feet.

require 'delegate'

sizeInInches = [ 10, 15, 22, 120 ]

class Feet < DelegateClass(Array)   def initialize(arr)     super(arr)   end   def [](*n)     val = super(*n)     case val.type     when Numeric; val/12.0     else;         val.collect {|i| i/12.0}     end   end end

sizeInFeet = Feet.new(sizeInInches)
sizeInInches[0..3] [10, 15, 22, 120]
sizeInFeet[0..3] [0.8333333333, 1.25, 1.833333333, 10.0]

Example 2. Use subclass SimpleDelegator when you want an object that both has its own behavior and delegates to different objects during its lifetime. This is an example of the State pattern. Objects of class TicketOffice sell tickets if a seller is available, or tell you to come back tomorrow if there is no seller.

require 'delegate'

class TicketSeller   def sellTicket()     return 'Here is a ticket'   end end

class NoTicketSeller   def sellTicket()     "Sorry-come back tomorrow"    end end

class TicketOffice < SimpleDelegator   def initialize     @seller = TicketSeller.new     @noseller = NoTicketSeller.new     super(@seller)   end   def allowSales(allow = true)     __setobj__(allow ? @seller : @noseller)     allow   end end

to = TicketOffice.new
to.sellTicket "Here is a ticket"
to.allowSales(false) false
to.sellTicket "Sorry-come back tomorrow"
to.allowSales(true) true
to.sellTicket "Here is a ticket"

Example 3. Create SimpleDelegator objects when you want a single object to delegate all its methods to two or more other objects.

# Example 3 - delegate from existing object
seller   = TicketSeller.new
noseller = NoTicketSeller.new
to = SimpleDelegator.new(seller)
to.sellTicket "Here's a ticket"
to.sellTicket "Here's a ticket"
to.__setobj__(noseller)
to.sellTicket "Sorry-come back tomorrow"
to.__setobj__(seller)
to.sellTicket "Here's a ticket"

Library: observer

The Observer pattern, also known as Publish/Subscribe, provides a simple mechanism for one object to inform a set of interested third-party objects when its state changes.

In the Ruby implementation, the notifying class mixes in the Observable module, which provides the methods for managing the associated observer objects.

add_observer(obj) Add obj as an observer on this object. obj will now receive notifications.
delete_observer(obj) Delete obj as an observer on this object. It will no longer receive notifications.
delete_observers Delete all observers associated with this object.
count_observers Return the count of observers associated with this object.
changed(newState=true) Set the changed state of this object. Notifications will be sent only if the changed state is true.
changed? Query the changed state of this object.
notify_observers(*args) If this object's changed state is true, invoke the update method in each currently associated observer in turn, passing it the given arguments. The changed state is then set to false.

The observers must implement the update method to receive notifications.

require "observer"

  class Ticker # Periodically fetch a stock price     include Observable

    def initialize(symbol)       @symbol = symbol     end

    def run       lastPrice = nil       loop do         price = Price.fetch(@symbol)         print "Current price: #{price}\n"         if price != lastPrice           changed                 # notify observers           lastPrice = price           notify_observers(Time.now, price)         end       end     end   end

  class Warner     def initialize(ticker, limit)       @limit = limit       ticker.add_observer(self)   # all warners are observers     end   end

  class WarnLow < Warner     def update(time, price)       # callback for observer       if price < @limit         print "--- #{time.to_s}: Price below #@limit: #{price}\n"       end     end   end

  class WarnHigh < Warner     def update(time, price)       # callback for observer       if price > @limit         print "+++ #{time.to_s}: Price above #@limit: #{price}\n"       end     end   end

ticker = Ticker.new("MSFT") WarnLow.new(ticker, 80) WarnHigh.new(ticker, 120) ticker.run
produces:
Current price: 83
Current price: 75
--- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
Current price: 90
Current price: 134
+++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
Current price: 134
Current price: 112
Current price: 79
--- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79

Library: singleton

The Singleton design pattern ensures that only one instance of a particular class may be created.

The singleton library makes this simple to implement. Mix the Singleton module into each class that is to be a singleton, and that class's new method will be made private. In its place, users of the class call the method instance, which returns a singleton instance of that class.

In this example, the two instances of MyClass are the same object.

require 'singleton'
class MyClass
  include Singleton
end
a = MyClass.instance #<MyClass:0x401b4ca8>
b = MyClass.instance #<MyClass:0x401b4ca8>


Ruby Programming
Previous Page Home Next Page

 
 
  Published under the terms of the Open Publication License Design by Interspire