CSC/ECE 517 Fall 2007/wiki2 c9: Difference between revisions
No edit summary |
No edit summary |
||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
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.) | |||
[[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 | |||
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 |
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