CSC/ECE 517 Fall 2007/wiki2 c9: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
CSC- 517 Object-Oriented Languages and Systems
Closures can be used to implement various design patterns such as Observer Pattern & Decorator Pattern
                                      '''Wiki Assignment'''


'''Topic 2 :  There are plenty of examples of method_missing on the Web. Unfortunately, I find most of them rather difficult to understand. One needs to look at quite a bit of source code and figure out what it does. Fix this by giving a plain-English description of several uses of method_missing not covered in our class, with Web links to the pages where you found them.'''


== Observer Pattern ==


Ruby has a powerful way to intercept calls to undefined method. This is implemented by defining method_missing in our class definition. If a call is made to an undefined method on a object, Ruby passes the name of the method and its arguments to the method_missing.  This is an efficient and safest way to intercept calls made to unknown methods and handles them in a proper fashion.
The observer pattern (sometimes known as publish/subscribe) is a design pattern used in computer programming to observe the state of an object in a program. It is related to the principle of implicit invocation.
Let us look into few examples (Source: http://rubylearning.com/satishtalim/ruby_method_missing.html
class Dummy 
def method_missing(m, *args) 
puts "There's no method called #{m} here, please try again." 
end 
end 
Dummy.new.anything 
>ruby tmp.rb 
Output: There's no method called anything here, please try again.   
In the above example, a call is made to a method called anything on object of class Dummy. There is no method called “anything” defined within the class. Ruby passes the name “anything” to method_missing. Hence the above output is displayed.  


In the following example, method method_missing intercepts and prints out a message with method name and argument list (if any) and forwards on the method call using 'send' method without knowing anything about the object passed to it. (source: http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html)
This pattern is mainly used to implement a distributed event handling system. In some programming languages, the issues addressed by this pattern are handled in the native event handling syntax. This is a very interesting feature in terms of real-time deployment of applications.


class SimpleCallLogger
The essence of this pattern is that one or more objects (called observers or listeners) are registered (or register themselves) to observe an event that may be raised by the observed object (the subject). (The object that may raise an event generally maintains a collection of the observers.)
   def initialize(o)
 
     @obj = o
[[Image:Image1.jpg]]
 
It is easy to implement Observer Pattern in Ruby using Closures.
 
Basic Implementation of Observer Pattern
 
class Observable
   def initialize
     @listeners = []
   end
   end
   def method_missing(methodname, *args)
 
    puts "called: #{methodname}(#{args})"
   def register_listener(listener)
     a = @obj.send(methodname, *args)
     @listeners << listener
    puts "\t-> returned: #{a}"
    return a
   end
   end
end


Another use of method_missing: (Source: http://redhanded.hobix.com/inspect/theBestOfMethod_missing.html)
  def unregister_listener(listener)
    @listeners.remove(listener)
  end


def method_missing(method)
  def run
     @posts = User.find_first(['username = ?', method.to_s]).posts
     notify_listeners("Hello!")
    render_action 'index'   
  end
    cache_page if ActionController::Base.perform_caching
end


The above code is an example of a controller used for generating RSS and Atom Feeds to user.  It is used by Elite Journal which is a multi user. Since their web page changes display every now and then depending upon the status of user login. Therefore caching the output is very hard. Therefore, most of the time the output is not cached. So, generally the RSS and Atom Feeds are cached.  Since Elite Journal is multi-user, there is a feed for each user. This feed is combined with the method_missing call such that a call to the /rss/scott URL will retrieve the feed to the user “Scott”. In the above example, the page is cached if caching is enabled by the action controller.
  protected
Another set of example for the use of method_missing is as follows: (Source: facets.rubyforge.org/src/lib/facets/core/kernel/as.rb)
  def notify_listeners(event)
def method_missing(sym, *args, &blk)
    @listeners.each {|l| l.notify(event) }
  @ancestor.instance_method(sym).bind(@subject).call(*args,&blk)
  end
end
end
require 'rubygems'
require 'facets'


module James
class Listener
   def name
   def initialize(observable)
     "James"
     observable.register_listener(self)
   end
   end
end


module Lynn
   def notify(event)
   def name
     puts "Notified of '#{event}'"
     "Lynn"
   end
   end
end
end


class FamilyMember
observable = Observable.new
  include James
listener = Listener.new(observable)
  include Lynn
observable.run                        #=> Notified of 'Hello!'
end


FamilyMember.ancestors # => [FamilyMember, Lynn, James, Object, Kernel]
member = FamilyMember.new
member.name # => "Lynn"
member.as(James).name # => "James"


In above example method_missing definition allows you to call a method on any ancestor. Initially instance of FamilyMember i.e member receives a message as which returns an instance of As. After returning the instance of As, member receives messae “name”. Since As does not contain the definition of name, the method_missing is called where in name is passed as an arguments. Within the method, instance_method is called on the ancestor with the name as the symbol. The instance_method will return the unbound method name since it is not defined. Then, method_missing binds the name to the subject which is member (instance of FamilyMember) and sends a call message along with the arguments passed before. Here, since name binds to the member, it can access the state or behavior of the member.
== Decorator Pattern ==
Last example for the purpose of using method_missing. (source: http://services.tucows.com/developers/2007/08/03/rubys-method_missing-method-explained-with-lolcats/)
class LolCat


   def confess
The Decorator Pattern is used for adding additional functionality to a particular object as opposed to a class of objects. It is easy to add functionality to an entire class of objects by subclassing an object, but it is impossible to extend a single object this way. With the Decorator Pattern, you can add functionality to a single object and leave others like it unmodified.
     puts “I made you a cookie…but then I eated it.
A Decorator, also known as a Wrapper, is an object that has an interface identical to an object that it contains. Any calls that the decorator gets, it relays to the object that it contains, and adds its own functionality along the way, either before or after the call. This gives you a lot of flexibility, since you can change what the decorator does at runtime, as opposed to having the change be static and determined at compile time by subclassing. Since a Decorator complies with the interface that the object that it contains, the Decorator is indistinguishable from the object that it contains.  That is, a Decorator is a concrete instance of the abstract class, and thus is indistinguishable from any other concrete instance, including other decorators.  This can be used to great advantage, as you can recursively nest decorators without any other objects being able to tell the difference, allowing a near infinite amount of customization.
Decorators add the ability to dynamically alter the behavior of an object because a decorator can be added or removed from an object without the client realizing that anything changed.  It is a good idea to use a Decorator in a situation where you want to change the behaviour of an object repeatedly (by adding and subtracting functionality) during runtime.   
The dynamic behavior modification capability also means that decorators are useful for adapting objects to new situations without re-writing the original object's code.
First let’s define our basic Window Class
class Window
   def draw
     # do some drawing here...
   end
   end
end
Given ruby's duck-typing nature, we could easily create a VerticalScrollWindow that wraps the original Window when we create the original window object, and pass that around. In fact we could patch only the single method and add a method_missing implementation that always delegated to the original Window.
class VerticalScrollWindow


   def eat
   def initialize(window)
     puts “NOM NOM NOM.”
     @window = window
   end
   end


   def method_missing(method)
   def draw
     puts “Oh noes! I has no idea how to #{method}.
     draw_vertical_scrollbar
    window.draw
   end
   end


  def method_missing(method, *args, &block)
    @window.send(method, *args, &block)
  end
end
end
> kitty = LolCat.new
=> #<LolCat:0x349a40>
> kitty.confess
I made you a cookie...but then I eated it.
=> nil
> kitty.eat
NOM NOM NOM.
=> nil
> kitty.poop
Oh noes! I has no idea how to poop.
=> nil
In the above example, we create an instance of LolCat which is named kitty. Next, call the method “confess” on kitty. Since it is defined , the method “confess” is called and appropriate result is printed. When the method poop is called, the method_missing is called since the method “poop” is not defined. Ruby passes the name of the method i.e “confess” and arguments that are needed to the method “poop”. The method_missing displays an error accordingly.

Latest revision as of 01:38, 2 October 2007

Closures can be used to implement various design patterns such as Observer Pattern & Decorator Pattern


Observer Pattern

The observer pattern (sometimes known as publish/subscribe) is a design pattern used in computer programming to observe the state of an object in a program. It is related to the principle of implicit invocation.

This pattern is mainly used to implement a distributed event handling system. In some programming languages, the issues addressed by this pattern are handled in the native event handling syntax. This is a very interesting feature in terms of real-time deployment of applications.

The essence of this pattern is that one or more objects (called observers or listeners) are registered (or register themselves) to observe an event that may be raised by the observed object (the subject). (The object that may raise an event generally maintains a collection of the observers.)


It is easy to implement Observer Pattern in Ruby using Closures.

Basic Implementation of Observer Pattern

class Observable

 def initialize
   @listeners = []
 end
 def register_listener(listener)
   @listeners << listener
 end
 def unregister_listener(listener)
   @listeners.remove(listener)
 end
 def run
   notify_listeners("Hello!")
 end
 protected
 def notify_listeners(event)
   @listeners.each {|l| l.notify(event) }
 end

end

class Listener

 def initialize(observable)
   observable.register_listener(self)
 end
 def notify(event)
   puts "Notified of '#{event}'"
 end

end

observable = Observable.new listener = Listener.new(observable) observable.run #=> Notified of 'Hello!'


Decorator Pattern

The Decorator Pattern is used for adding additional functionality to a particular object as opposed to a class of objects. It is easy to add functionality to an entire class of objects by subclassing an object, but it is impossible to extend a single object this way. With the Decorator Pattern, you can add functionality to a single object and leave others like it unmodified. A Decorator, also known as a Wrapper, is an object that has an interface identical to an object that it contains. Any calls that the decorator gets, it relays to the object that it contains, and adds its own functionality along the way, either before or after the call. This gives you a lot of flexibility, since you can change what the decorator does at runtime, as opposed to having the change be static and determined at compile time by subclassing. Since a Decorator complies with the interface that the object that it contains, the Decorator is indistinguishable from the object that it contains. That is, a Decorator is a concrete instance of the abstract class, and thus is indistinguishable from any other concrete instance, including other decorators. This can be used to great advantage, as you can recursively nest decorators without any other objects being able to tell the difference, allowing a near infinite amount of customization. Decorators add the ability to dynamically alter the behavior of an object because a decorator can be added or removed from an object without the client realizing that anything changed. It is a good idea to use a Decorator in a situation where you want to change the behaviour of an object repeatedly (by adding and subtracting functionality) during runtime. The dynamic behavior modification capability also means that decorators are useful for adapting objects to new situations without re-writing the original object's code. First let’s define our basic Window Class class Window

 def draw
   # do some drawing here...
 end

end Given ruby's duck-typing nature, we could easily create a VerticalScrollWindow that wraps the original Window when we create the original window object, and pass that around. In fact we could patch only the single method and add a method_missing implementation that always delegated to the original Window. class VerticalScrollWindow

 def initialize(window)
   @window = window
 end
 def draw
   draw_vertical_scrollbar
   window.draw
 end
 def method_missing(method, *args, &block)
   @window.send(method, *args, &block)
 end

end