CSC/ECE 517 Fall 2007/wiki1b 3 an

From Expertiza_Wiki
Jump to navigation Jump to search

We have said that closures in Ruby can be used to implement the Command and Strategy patterns. Closures are useful in implementing various other design patterns too. Explain which patterns, and give examples, using pseudo-Ruby code (i.e., your code should illustrate how to solve the problem with closures in Ruby, but it doesn't actually have to run, if it would be too complicated to give a running example).


Closures

What are Closures?

Every ruby block is a closure in the sense that it carries around the context in which it was defined. This means that a closure can reference the variables that were in scope when it was defined, even if those variables later go out of scope.

def closure
 x = "a_local_variable"
 return proc { puts x } # when this line gets executed, the proc takes a snapshot
                        # of the embedding scope: in this case a local variable
end
closure.call # => a_local_variable

This enables us to move around these blocks throughout our code without having to worry about the context. In addition to that, we can also pass a closure to another method which enables us to customize the behavior of the method.

Thus a closure is a block of code that has these three properties

• It can be passed around as a value.

• It can be executed on demand by any procedure or method that has that value.

• It can refer to variables from the context in which it was created (it is “closed” with respect to variable access).

A Simple Closure Example

def remember(&a_block)
 @block = a_block
end
# Invoke the above method, giving it a block that takes a name.
remember {|name| puts "Hello, #{name}!"}
# When the time is right (for the object) -- call the closure!
@block.call("John")
# => "Hello, John!"

Advantages of Closures

• Designers of software libraries can allow users to customize behavior by passing closures as arguments to important functions.For example, a function that sorts values can accept a closure argument that compares the values to be sorted according to a user-defined criterion.

• Because closures delay evaluation—i.e., they do not "do" anything until they are called—they can be used to define control structures.


Closures And Design Patterns

When implementing design patterns, it is often useful to pass around code that needs to be executed later.Using the power of closures,a number of design patterns can be implemented in Ruby in a simpler way as compared to other languages.Command and Strategy patterns are usually implemented using proc objects which are nothing but closures.In the following sections we discuss the use of closures in implementing Observer and Iterator patterns along with pseudocodes for the same.

Observer Pattern

Description

The observer pattern is used to observe the state of an object in a program. An observer pattern defines a one to many dependency between objects so that when one object changes state all its dependents are notified and updated accordingly

Typical Uses

Observer pattern is mainly used to implement a distributed event handling system where objects are listening for an external event or for changes of the value of an object property.

Examples

• In a mailing list,where every time an event happens (a new product,a gathering etc) a message is sent to the people subscribed to the list.

• A change in the data values triggers change in the bar graph pie chart representations in a spreadsheet.

Terminology

In its simplified form, the observer design pattern consists of a subject,and one or more observers,which observe an event in the subject.An event can be a change in the state of the subject and can be defined accordingly.

Subject

This class provides an interface for attaching and detaching observers.Contains the following functions

• Attach – Adds a new observer to the list of observers observing the subject

• Detach – Deletes an observer from the list of observers observing the subject

• Notify – Notifies each observer by calling the notify function in the observer,whenever a change occurs.

ConcreteSubject

This class provides the state of interest to observers. It also sends a notification to all observers, by calling the Notify function in its super class (i.e, in the Subject class). It contains the following function:

GetState - Returns the state of the subject

Observer

This class defines an updating interface for all observers, to receive update notification from the subject. The Observer class is used as an abstract class to implement concrete observers. It contains the following function:

Notify - An abstract function, to be overridden by concrete observers.

ConcreteObserver

This class maintains a reference with the ConcreteSubject, to receive the state of the subject when a notification is received. It contains the following function:

Notify - This is the overridden function in the concrete class. When this function is called by the subject, the ConcreteObserver calls the GetState function of the subject to update the information it has about the subject's state.

Structure

Closures and Observer Pattern

Closures can be used to implement Observer pattern.The role of closures can be the basis of the following two applications of Observer design patterns

• A change in one object requires changing other objects.Also the number of objects to be changed is unknown.

• The object being changed should be able to notify other objects about the change without having any idea about the current state of the objects.


Pseudocode

class Subject
  def initialize
    @list=[]
  end
  def attach(&list)
    @list << list
  end
  def notify
    @list.each{|x| x.call(@radius)}
  end
  def update(r)
    @radius=r
    notify
  end
end


class Observer1
  def calc_area(radius)
    @area=radius*radius*3.14
    puts "Area #{@area}"
  end
end


class Observer2
 def calc_circumference(radius)
    @circum=2*3.14*radius
    puts "Circumference #{@circum}"
 end
end


o1=Observer1.new
o2=Observer2.new
s=Subject.new
s.attach { |s| o1.calc_area(s)}
s.attach { |s| o2.calc_circumference(s)}
s.update(5)


The output is
Area 78.5
Circumference 31.4


Here we have a subject class that contains the following methods.

• Attach()

This method is used to update the list of observers

• Update()

This method updates the radius and calls the notify method.

• Notify()

This method notifies all the observers in the list about the event which is the change in the value of the radius.

We have two observers for calculating area and circumference. On receiving notify,these functions return updated values of area and circumference depending on the new value of the radius.


Iterator Pattern

Description

The Iterator pattern is a design pattern in which iterators are used to access the elements of an aggregate object sequentially without exposing its underlying representation. An Iterator object encapsulates the internal structure of how the iteration occurs.An aggregate function can be an array or a hash.

Typical Uses

• to access an aggregate object's contents without exposing its internal representation.

• to support multiple traversals of aggregate objects.

• to provide a uniform interface for traversing different aggregate structures

Examples

A tree, linked list, hash table, and an array all need to be iterated with Search, Sort, Next.Using the iterator pattern will reduce the number of redundant procedures written for each aggregate object.

Terminology

• Iterator -defines an interface for accessing and traversing elements.

• ConcreteIterator -implements the Iterator interface and keeps track of the current position in the traversal of the aggregate.

• Aggregate - defines an interface for creating an Iterator object.

• ConcreteAggregate - implements the Iterator creation interface to return an instance of the proper ConcreteIterator.


Structure


Closures and Iterator Pattern

Pseudocode 1

Following is a simple example which illustrates the iterator pattern using the power of Ruby closures.

def fibonacci()
  while x <= max
  yield x
  x, y = y, x + y
  end
end
 fibonacci 30 { |x| print x, " " } # -> 1 1 2 3...
 y = 0
 fibonacci 30 {|x| y += x }
 puts y # -> 54


In this code the function fibonacci is used to print out the elements of the series. The number of elements to be printed depends on the argument x. The function is also used to calculate the sum of the x elements. Closures are used to implement both the functionalities as shown. The variables x and y are referenced even after they go out of scope.


Pseudocode 2

Here is another pseudocode which illustrates an implementation of the iterator pattern using closures.

def find_birthdays_in_range(min,max)
   birthdays = [ "1970-06-15", "1975-08-13", "1939-03-21", "2001-12-01" ]
   birthdays.find_all { |birthday| birthday > min && birthday < max }
end


Here there are 3 functions here that are called from within each other.

1 : find_birthdays_in_range

2 : find_all

3 : anonymous block

The third function is the closure.It accepts one parameter called birthday which is defined in the first function.It then iterates over the array(birthdays) and returns only those values in the given range,specified by min and max.

References

1. Programming Ruby:The Pragmatic Programmers Guide

2. Head First Design Patterns

3. Design Patterns - Elements Of Reusable Object Oriented Software

4. Ruby Cookbook

External Links

1. Ruby Standard Library Documentation

2. Features of Ruby

3. Closures in Ruby

4. Observer pattern in Ruby