CSC/ECE 517 Fall 2009/wiki2 11 sv: Difference between revisions
(114 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
= | <b>Other Design Patterns in Ruby</b> | ||
The idea of design pattern was first introduced by the architect [http://en.wikipedia.org/wiki/Christopher_Alexander Christopher Alexander] in the field of architecture. Later it has been adapted for various other disciplines, including | = Design patterns = | ||
==Introduction== | |||
The idea of design pattern was first introduced by the architect [http://en.wikipedia.org/wiki/Christopher_Alexander Christopher Alexander] in the field of architecture. Later it has been adapted for various other disciplines, including Computer Science. Christopher Alexander says, "Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice". Even though Alexander was talking about patterns in buildings and towns, what he says is true about Object-Oriented Design Patterns. Our solutions are expressed in terms of objects and interfaces instead of walls and doors, but at the core of both the kinds of patterns is a solution to a problem in a context. | |||
In general, a pattern has four essential elements: | In general, a pattern has four essential elements: | ||
Line 6: | Line 9: | ||
#The '''pattern name''' is a handle used to describe a design problem, its solutions, and consequences in a word or two. | #The '''pattern name''' is a handle used to describe a design problem, its solutions, and consequences in a word or two. | ||
#The '''problem''' explains the problem and its context and also describes when to apply the pattern. | #The '''problem''' explains the problem and its context and also describes when to apply the pattern. | ||
#The '''solution''' describes the elements that make up the design, their relationships, responsibilities, and collaborations. | #The '''solution''' describes the elements that make up the design, their relationships, responsibilities, and collaborations. The pattern provides an abstract description of a design problem and how a general arrangement of elements solves it. | ||
#The '''consequences''' are the results and trade-offs of applying the pattern. | #The '''consequences''' are the results and trade-offs of applying the pattern. | ||
A design pattern is a formal way of documenting a solution to a design problem in a particular field of expertise. In software engineering, a design pattern is a solution to a general and commonly occurring problem in software design. A design pattern is not a finished design that can be transformed directly into code. But it is a description or template for how to solve a problem that can be used in many different situations.<br> | |||
==Design patterns in ruby== | |||
Almost every design pattern of ruby is borrowed from the [http://en.wikipedia.org/wiki/Gang_of_Four_%28software%29 GOF] book. Following is the list of common patterns used in ruby. | |||
Almost every design | |||
#Template | #Template | ||
Line 22: | Line 23: | ||
#[http://en.wikipedia.org/wiki/Composite_pattern Composite] | #[http://en.wikipedia.org/wiki/Composite_pattern Composite] | ||
#[http://en.wikipedia.org/wiki/Iterator_pattern Iterator] | #[http://en.wikipedia.org/wiki/Iterator_pattern Iterator] | ||
#[http://en.wikipedia.org/wiki/Command_pattern | #[http://en.wikipedia.org/wiki/Command_pattern Command] | ||
#[http://en.wikipedia.org/wiki/Adapter_pattern Adapter] | #[http://en.wikipedia.org/wiki/Adapter_pattern Adapter] | ||
#Proxy | #Proxy | ||
Line 31: | Line 32: | ||
#[http://en.wikipedia.org/wiki/Interpreter_pattern Interpreter] | #[http://en.wikipedia.org/wiki/Interpreter_pattern Interpreter] | ||
The essence of the [http://en.wikipedia.org/wiki/Factory_method_pattern Factory pattern] is to "Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses. | ==Factory pattern== | ||
The essence of the [http://en.wikipedia.org/wiki/Factory_method_pattern Factory pattern] is to "Define an interface for creating an object, but let the subclasses decide which class to instantiate". The Factory method lets a class defer instantiation to subclasses. | |||
[http://en.wikipedia.org/wiki/Factory_method_pattern Factory pattern] is used when | [http://en.wikipedia.org/wiki/Factory_method_pattern Factory pattern] is used when | ||
# | #A class can't anticipate the class of objects it must create. | ||
# | #A class wants its subclasses to specify the objects it creates. | ||
# | #Classes delegate responsibility to one of the several helper subclasses, and we want to localize the knowledge of which helper subclass is the delegate. | ||
Let us try to understand [http://en.wikipedia.org/wiki/Factory_method_pattern Factory] design pattern with the help of a real world problem. Imagine that we are asked to build a simulation of life in a pond. In particular, we need to model the coming and going of the ducks. So we sit down and write a class to model the ducks: | |||
Adapted from the class notes. | Adapted from the class notes. | ||
Line 61: | Line 62: | ||
But ducks also need a place to live(Which we didn't | But ducks also need a place to live(Which we didn't implement in class), and for that we build a Pond class: | ||
<pre> | <pre> | ||
Line 80: | Line 81: | ||
Life | Life in the pond continues idyllically until one dark day when we get a request to model a different denizen of the puddle: the frog. Now it is easy enough to create a Frog class that sports exactly the same interface as the ducks: | ||
<pre> | <pre> | ||
Line 100: | Line 98: | ||
But there is a problem with the Pond class—right there in the initialize method we are explicitly creating '''ducks'''. And now we have exactly understood the problem which factory method solves. | But there is a problem with the Pond class—right there in the initialize method we are explicitly creating '''ducks'''. And now we have exactly understood the problem which factory method solves. | ||
Rewriting the factory adopted version of the | Rewriting the factory adopted version of the Pond class. | ||
<pre> | <pre> | ||
Line 119: | Line 116: | ||
</pre> | </pre> | ||
Next we can build two subclasses of | Next we can build two subclasses of Pond — one for a pond full of ducks and the other for a pond hopping with frogs: | ||
<pre> | <pre> | ||
Line 144: | Line 141: | ||
==Observer pattern== | |||
The [http://en.wikipedia.org/wiki/Observer_pattern Observer pattern](sometimes known as publish/subscribe) is a software design pattern in which an object, called the 'subject', maintains a list of its dependents, called 'observers', and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems. | The [http://en.wikipedia.org/wiki/Observer_pattern Observer pattern](sometimes known as publish/subscribe) is a software design pattern in which an object, called the 'subject', maintains a list of its dependents, called 'observers', and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems. | ||
<br> | <br> | ||
The [http://en.wikipedia.org/wiki/Observer_pattern Observer pattern] is used when: | The [http://en.wikipedia.org/wiki/Observer_pattern Observer pattern] is used when: | ||
# | #An [http://en.wikipedia.org/wiki/Abstraction_%28computer_science%29 abstraction] has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets us vary and reuse them independently.<br> | ||
# | #A change to one object requires changing others, and we do not know how many objects need to be changed.<br> | ||
# | #An object should be able to notify other objects without making assumptions about who these objects are. In other words, we don't want these objects tightly coupled.<br> | ||
<br> | <br> | ||
<b>Example Scenario where Observer Pattern | <b>Example Scenario where Observer Pattern can be Used:</b><br> | ||
Consider a personnel system where an employee's salary changes and the payroll department needs to know when these changes take place. Here, | |||
Consider a personnel system where an employee's salary changes and the payroll department needs to know when these changes take place. Here, the problem is as how can one make the Employee object spread the news about salary changes without tangling it up with the payroll system?<br> | |||
In such a situation, Observer pattern is used. Initially, an object is created that is interested in the state of a person's(any employee's) finances. This object then needs to simply register with that person's 'Employee Object' ahead of time. Once registered, that object would receive timely updates about the ups and downs of the person's paycheck.<br> | In such a situation, Observer pattern is used. Initially, an object is created that is interested in the state of a person's(any employee's) finances. This object then needs to simply register with that person's 'Employee Object' ahead of time. Once registered, that object would receive timely updates about the ups and downs of the person's paycheck.<br> | ||
Line 168: | Line 161: | ||
<pre> | <pre> | ||
class Employee | class Employee | ||
attr_reader :name | |||
attr_accessor :title, :salary | |||
def initialize(name, title, salary) | |||
@name = name | |||
@title = title | |||
@salary = salary | |||
end | |||
end | end | ||
</pre>The employees can get raises because we made the salary field accessible with attr_accessor.<br> | </pre>The employees can get raises because we made the salary field accessible with attr_accessor.<br> | ||
<pre> | <pre> | ||
jim = Employee.new("Jim Flintstone", "Crane Operator", 5000.0) | |||
# Give Jim a raise | |||
jim.salary=8000.0 | |||
</pre>Now, adding some code to keep the payroll department informed of pay changes:<br> | </pre>Now, adding some code to keep the payroll department(observer class) informed of pay changes:<br> | ||
<pre> | <pre> | ||
class Payroll | class Payroll | ||
def update( changed_employee ) | |||
puts("Cut a new check for #{changed_employee.name}!") | puts("Cut a new check for #{changed_employee.name}!") | ||
puts("His salary is now #{changed_employee.salary}!") | puts("His salary is now #{changed_employee.salary}!") | ||
Line 193: | Line 186: | ||
<pre> | <pre> | ||
class Employee | class Employee | ||
attr_reader :name, :title | |||
attr_reader :salary | |||
def initialize( name, title, salary,payroll) | |||
@name = name | @name = name | ||
@title = title | @title = title | ||
@salary = salary | @salary = salary | ||
@payroll = payroll | @payroll = payroll | ||
end | |||
def salary=(new_salary) | |||
@salary = new_salary | @salary = new_salary | ||
@payroll.update(self) | @payroll.update(self) | ||
end | |||
end | end | ||
</pre> | </pre> | ||
We can now change Jim's wages as follows:<br> | We can now change Jim's wages as follows:<br> | ||
<pre> | <pre> | ||
payroll = Payroll.new | |||
jim = Employee.new('Jim', 'Crane Operator', 5000, payroll) | |||
jim.salary = 8000 | |||
</pre> | </pre> | ||
And the payroll department will know about these changes. The output of the above code is: | And the payroll department will know about these changes. The output of the above code is: | ||
<pre> | <pre> | ||
Cut a new check for Jim! | Cut a new check for Jim! | ||
His salary is now 8000! | His salary is now 8000! | ||
</pre> | </pre> | ||
<b>Improvising the above code:</b> | <b>Improvising the above code:</b> | ||
Line 224: | Line 217: | ||
The general way to solve this problem is to separate out the thing that is changing. We can set up an array for list of objects(in the initialize method) that are interested in hearing about the latest news from the Employee object. | The general way to solve this problem is to separate out the thing that is changing. We can set up an array for list of objects(in the initialize method) that are interested in hearing about the latest news from the Employee object. | ||
<pre> | <pre> | ||
def initialize( name, title, salary ) | def initialize(name, title, salary) | ||
@name = name | |||
@title = title | |||
@salary = salary | |||
@observers = [] | |||
end | end | ||
</pre> | </pre> | ||
Line 234: | Line 227: | ||
<pre> | <pre> | ||
def salary=(new_salary) | def salary=(new_salary) | ||
@salary = new_salary | |||
notify_observers | |||
end | end | ||
def notify_observers | def notify_observers | ||
@observers.each do |observer| | |||
observer.update(self) | |||
end | |||
end | end | ||
</pre> | </pre> | ||
The key moving part of notify_observers is observer.update(self). This bit of code calls the update method on each observer, telling it that something (in this case, the salary) has changed on the Employee object. | The key moving part of notify_observers is '''''observer.update(self)'''''. This bit of code calls the update method on each observer, telling it that something (in this case, the salary) has changed on the Employee object. | ||
<br> | <br> | ||
Line 249: | Line 243: | ||
<pre> | <pre> | ||
def add_observer(observer) | def add_observer(observer) | ||
@observers << observer | |||
end | end | ||
def delete_observer(observer) | def delete_observer(observer) | ||
@observers.delete(observer) | @observers.delete(observer) | ||
end | |||
</pre> | |||
Putting all the pieces of the observer code discussed above : | |||
<pre> | |||
class Subject | |||
def initialize | |||
@observers=[] | |||
end | |||
def add_observer(observer) | |||
@observers << observer | |||
end | |||
def delete_observer(observer) | |||
@observers.delete(observer) | |||
end | |||
def notify_observers | |||
@observers.each do |observer| | |||
observer.update(self) | |||
end | |||
end | |||
end | |||
</pre> | |||
And correspondingly the employee will inherit the subject as below: | |||
<pre> | |||
class Employee < Subject | |||
attr_reader :name, :address | |||
attr_reader :salary | |||
def initialize(name, title, salary) | |||
super() | |||
@name = name | |||
@title = title | |||
@salary = salary | |||
end | |||
def salary=(new_salary) | |||
@salary = new_salary | |||
notify_observers | |||
end | |||
end | end | ||
</pre> | </pre> | ||
Now any object that is interested in hearing about changes in Jim's salary can simply register as an observer on Jim's Employee object:<br> | Now any object that is interested in hearing about changes in Jim's salary can simply register as an observer on Jim's Employee object:<br> | ||
<pre> | <pre> | ||
jim = Employee.new('Jim', 'Crane Operator', 5000.0) | |||
payroll = Payroll.new | |||
jim.add_observer( payroll ) | |||
jim.salary = 9000.0 | |||
</pre> | </pre> | ||
Hence by building this general mechanism, we have removed the implicit coupling between the Employee class and the Payroll object. Employee no longer cares which or how many other objects are interested in knowing about salary changes; it just forwards the news to any object that said that it was interested. | Hence by building this general mechanism, we have removed the implicit coupling between the Employee class and the Payroll object. Employee no longer cares which or how many other objects are interested in knowing about salary changes; it just forwards the news to any object that said that it was interested. | ||
In addition, instances of the Employee class will be happy with no observers, one, or several observers. | In addition, instances of the Employee class will be happy with no observers, one, or several observers. | ||
Now the payroll department will hear about the changes as follows: | |||
<pre> | <pre> | ||
Cut a new check for Jim! | |||
His salary is now 9000.0! | |||
</pre> | </pre> | ||
==Conclusion== | |||
===References | Building patterns in Ruby is easier for a number of reasons as follows: | ||
#Ruby is dynamically typed. By dispensing with static typing, Ruby dramatically reduces the code overhead of building most programs, including those that implement patterns. | |||
#Ruby has code closures. It allows us to pass around chunks of code and associated scope without having to laboriously construct entire classes and objects that do nothing else. | |||
#Ruby classes are real objects. Because a class in Ruby is just another object, we can do any of the usual runtime things to a Ruby class that we can do to any other object: We can create totally new classes. We can modify existing classes by adding or deleting methods. We can even clone a class and change the copy, leaving the original alone. | |||
#Ruby has an elegant system of code reuse. | |||
In addition to supporting garden-variety inheritance, Ruby allows us to define mixins, which are a simple but flexible way to write code that can be shared among several classes. All of this makes code in Ruby compressible. In Ruby, as in Java and C++, we can implement very sophisticated ideas, but with Ruby it becomes possible to hide the details of our implementations much more effectively. Hence, with all the above mentioned features, we can say that Ruby can implement the design patterns more efficiently or transparently than static (or other dynamic) Object-Oriented languages. | |||
==See also== | |||
[1] [http://en.wikipedia.org/wiki/Abstraction_%28computer_science%29 Abstraction] | |||
[2] [http://en.wikipedia.org/wiki/Strategy_pattern Strategy] | |||
[3] [http://en.wikipedia.org/wiki/Observer_pattern Observer] | |||
[4] [http://en.wikipedia.org/wiki/Composite_pattern Composite] | |||
[5] [http://en.wikipedia.org/wiki/Iterator_pattern Iterator] | |||
[6] [http://en.wikipedia.org/wiki/Command_pattern Commands] | |||
[7] [http://en.wikipedia.org/wiki/Adapter_pattern Adapter] | |||
[8] [http://en.wikipedia.org/wiki/Factory_method_pattern Factory] | |||
==References== | |||
[1] [http://en.wikipedia.org/wiki/Gang_of_Four_%28software%29 Gang of Four] | [1] [http://en.wikipedia.org/wiki/Gang_of_Four_%28software%29 Gang of Four] | ||
Line 286: | Line 346: | ||
[3] [http://en.wikipedia.org/wiki/Observer_pattern Observer Design pattern] | [3] [http://en.wikipedia.org/wiki/Observer_pattern Observer Design pattern] | ||
[4] Design Patterns in Ruby Text Book | |||
Latest revision as of 02:35, 15 October 2009
Other Design Patterns in Ruby
Design patterns
Introduction
The idea of design pattern was first introduced by the architect Christopher Alexander in the field of architecture. Later it has been adapted for various other disciplines, including Computer Science. Christopher Alexander says, "Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice". Even though Alexander was talking about patterns in buildings and towns, what he says is true about Object-Oriented Design Patterns. Our solutions are expressed in terms of objects and interfaces instead of walls and doors, but at the core of both the kinds of patterns is a solution to a problem in a context.
In general, a pattern has four essential elements:
- The pattern name is a handle used to describe a design problem, its solutions, and consequences in a word or two.
- The problem explains the problem and its context and also describes when to apply the pattern.
- The solution describes the elements that make up the design, their relationships, responsibilities, and collaborations. The pattern provides an abstract description of a design problem and how a general arrangement of elements solves it.
- The consequences are the results and trade-offs of applying the pattern.
A design pattern is a formal way of documenting a solution to a design problem in a particular field of expertise. In software engineering, a design pattern is a solution to a general and commonly occurring problem in software design. A design pattern is not a finished design that can be transformed directly into code. But it is a description or template for how to solve a problem that can be used in many different situations.
Design patterns in ruby
Almost every design pattern of ruby is borrowed from the GOF book. Following is the list of common patterns used in ruby.
- Template
- Strategy
- Observer
- Composite
- Iterator
- Command
- Adapter
- Proxy
- Decorator
- Singleton
- Factory
- Builder
- Interpreter
Factory pattern
The essence of the Factory pattern is to "Define an interface for creating an object, but let the subclasses decide which class to instantiate". The Factory method lets a class defer instantiation to subclasses.
Factory pattern is used when
- A class can't anticipate the class of objects it must create.
- A class wants its subclasses to specify the objects it creates.
- Classes delegate responsibility to one of the several helper subclasses, and we want to localize the knowledge of which helper subclass is the delegate.
Let us try to understand Factory design pattern with the help of a real world problem. Imagine that we are asked to build a simulation of life in a pond. In particular, we need to model the coming and going of the ducks. So we sit down and write a class to model the ducks:
Adapted from the class notes.
class Duck def initialize(name) @name = name end def speak puts("Duck #{@name} says Quack!") end end
But ducks also need a place to live(Which we didn't implement in class), and for that we build a Pond class:
class Pond def initialize(number_ducks) @ducks = [] number_ducks.times do |i| duck = Duck.new("Duck#{i}") @ducks << duck end end def simulate_one_day @ducks.each {|duck| duck.speak} end end
Life in the pond continues idyllically until one dark day when we get a request to model a different denizen of the puddle: the frog. Now it is easy enough to create a Frog class that sports exactly the same interface as the ducks:
class Frog def initialize(name) @name = name end def speak puts("Frog #{@name} says Crooooaaaak!") end end
But there is a problem with the Pond class—right there in the initialize method we are explicitly creating ducks. And now we have exactly understood the problem which factory method solves.
Rewriting the factory adopted version of the Pond class.
class Pond def initialize(number_animals) @animals = [] number_animals.times do |i| animal = new_animal("Animal#{i}") @animals << animal end end def simulate_one_day @animals.each {|animal| animal.speak} end end
Next we can build two subclasses of Pond — one for a pond full of ducks and the other for a pond hopping with frogs:
class DuckPond < Pond def new_animal(name) Duck.new(name) end end class FrogPond < Pond def new_animal(name) Frog.new(name) end end
And can happily use them as below
pond = FrogPond.new(3) pond.simulate_one_day
Observer pattern
The Observer pattern(sometimes known as publish/subscribe) is a software design pattern in which an object, called the 'subject', maintains a list of its dependents, called 'observers', and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems.
The Observer pattern is used when:
- An abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets us vary and reuse them independently.
- A change to one object requires changing others, and we do not know how many objects need to be changed.
- An object should be able to notify other objects without making assumptions about who these objects are. In other words, we don't want these objects tightly coupled.
Example Scenario where Observer Pattern can be Used:
Consider a personnel system where an employee's salary changes and the payroll department needs to know when these changes take place. Here, the problem is as how can one make the Employee object spread the news about salary changes without tangling it up with the payroll system?
In such a situation, Observer pattern is used. Initially, an object is created that is interested in the state of a person's(any employee's) finances. This object then needs to simply register with that person's 'Employee Object' ahead of time. Once registered, that object would receive timely updates about the ups and downs of the person's paycheck.
Here is a basic version of an Employee object that tracks an employee. It does not have any code that tells about any salary updates.
class Employee attr_reader :name attr_accessor :title, :salary def initialize(name, title, salary) @name = name @title = title @salary = salary end end
The employees can get raises because we made the salary field accessible with attr_accessor.
jim = Employee.new("Jim Flintstone", "Crane Operator", 5000.0) # Give Jim a raise jim.salary=8000.0
Now, adding some code to keep the payroll department(observer class) informed of pay changes:
class Payroll def update( changed_employee ) puts("Cut a new check for #{changed_employee.name}!") puts("His salary is now #{changed_employee.salary}!") end end
class Employee attr_reader :name, :title attr_reader :salary def initialize( name, title, salary,payroll) @name = name @title = title @salary = salary @payroll = payroll end def salary=(new_salary) @salary = new_salary @payroll.update(self) end end
We can now change Jim's wages as follows:
payroll = Payroll.new jim = Employee.new('Jim', 'Crane Operator', 5000, payroll) jim.salary = 8000
And the payroll department will know about these changes. The output of the above code is:
Cut a new check for Jim! His salary is now 8000!
Improvising the above code: The trouble with the above code is that it is hard-wired to inform the payroll department alone about salary changes. This does not help in the situations where some other classes for e.g.,accounting-related classes needs to be informed about Jim's financial state. In such situation modifying employee class does not work out as nothing in the Employee class is really changing. The general way to solve this problem is to separate out the thing that is changing. We can set up an array for list of objects(in the initialize method) that are interested in hearing about the latest news from the Employee object.
def initialize(name, title, salary) @name = name @title = title @salary = salary @observers = [] end
Also we need the following code to inform all of the observers that something has changed:
def salary=(new_salary) @salary = new_salary notify_observers end def notify_observers @observers.each do |observer| observer.update(self) end end
The key moving part of notify_observers is observer.update(self). This bit of code calls the update method on each observer, telling it that something (in this case, the salary) has changed on the Employee object.
Now lets us consider writing methods that add and delete observers from the Employee object:
def add_observer(observer) @observers << observer end def delete_observer(observer) @observers.delete(observer) end
Putting all the pieces of the observer code discussed above :
class Subject def initialize @observers=[] end def add_observer(observer) @observers << observer end def delete_observer(observer) @observers.delete(observer) end def notify_observers @observers.each do |observer| observer.update(self) end end end
And correspondingly the employee will inherit the subject as below:
class Employee < Subject attr_reader :name, :address attr_reader :salary def initialize(name, title, salary) super() @name = name @title = title @salary = salary end def salary=(new_salary) @salary = new_salary notify_observers end end
Now any object that is interested in hearing about changes in Jim's salary can simply register as an observer on Jim's Employee object:
jim = Employee.new('Jim', 'Crane Operator', 5000.0) payroll = Payroll.new jim.add_observer( payroll ) jim.salary = 9000.0
Hence by building this general mechanism, we have removed the implicit coupling between the Employee class and the Payroll object. Employee no longer cares which or how many other objects are interested in knowing about salary changes; it just forwards the news to any object that said that it was interested. In addition, instances of the Employee class will be happy with no observers, one, or several observers.
Now the payroll department will hear about the changes as follows:
Cut a new check for Jim! His salary is now 9000.0!
Conclusion
Building patterns in Ruby is easier for a number of reasons as follows:
- Ruby is dynamically typed. By dispensing with static typing, Ruby dramatically reduces the code overhead of building most programs, including those that implement patterns.
- Ruby has code closures. It allows us to pass around chunks of code and associated scope without having to laboriously construct entire classes and objects that do nothing else.
- Ruby classes are real objects. Because a class in Ruby is just another object, we can do any of the usual runtime things to a Ruby class that we can do to any other object: We can create totally new classes. We can modify existing classes by adding or deleting methods. We can even clone a class and change the copy, leaving the original alone.
- Ruby has an elegant system of code reuse.
In addition to supporting garden-variety inheritance, Ruby allows us to define mixins, which are a simple but flexible way to write code that can be shared among several classes. All of this makes code in Ruby compressible. In Ruby, as in Java and C++, we can implement very sophisticated ideas, but with Ruby it becomes possible to hide the details of our implementations much more effectively. Hence, with all the above mentioned features, we can say that Ruby can implement the design patterns more efficiently or transparently than static (or other dynamic) Object-Oriented languages.
See also
[1] Abstraction
[2] Strategy
[3] Observer
[4] Composite
[5] Iterator
[6] Commands
[7] Adapter
[8] Factory
References
[1] Gang of Four
[4] Design Patterns in Ruby Text Book