CSC/ECE 517 Fall 2009/wiki2 11 sv: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 287: Line 287:
the ''subject class'' is the '''''Employee''''' class. The observers are the objects that are interested
the ''subject class'' is the '''''Employee''''' class. The observers are the objects that are interested
in getting the news. In our employee example, we have ''two observers'': '''''Payroll and
in getting the news. In our employee example, we have ''two observers'': '''''Payroll and
TaxMan.''''' When an object is interested in being informed of the state of the subject, it
TaxMan'''''. When an object is interested in being informed of the state of the subject, it
registers as an observer on that subject.
registers as an observer on that subject.



Revision as of 16:48, 9 October 2009

Design patterns

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 kinds of patterns is a solution to a problem in a context [1].

In general, a pattern has four essential elements:

  1. The pattern name is a handle used to describe a design problem, its solutions, and consequences in a word or two.
  2. The problem explains the problem and its context and also describes when to apply the pattern.
  3. 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.
  4. 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 patterns of ruby are borrowed from the GOF book. Following is the list of common pattern used in ruby.

  1. Template
  2. Strategy
  3. Observer
  4. Composite
  5. Iterator
  6. Commands
  7. Adapter
  8. Proxy
  9. Decorator
  10. Singleton
  11. Factory
  12. Builder
  13. 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

  1. a class can't anticipate the class of objects it must create.
  2. a class wants its subclasses to specify the objects it creates.
  3. classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate. [1]


Will 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 comings and goings 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 implemented 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 on 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 Pound 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:

  1. When an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets us vary and reuse them independently.
  2. When a change to one object requires changing others, and we do not know how many objects need to be changed.
  3. When 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 is Used:
Consider a personnel system where an employee's salary changes and the payroll department needs to know when these changes take place. Here, 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 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

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 = 7000.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.

class TaxMan
    def update( changed_employee )
        puts("Send #{changed_employee.name} a new tax bill!")
    end
end
tax_man = TaxMan.new
jim.add_observer(tax_man)

Suppose we change Jim’s salary again:
jim.salary=9000.0
--figure--
Now both the payroll department and the tax man will hear about it:
Cut a new check for Fred!
His salary is now 9000.0!
Send Fred a new tax bill!
The GoF called this idea of building a clean interface between the source of the news that some object has changed and the consumers of that news the Observer pattern (Refer above Figure).
The GoF called the class with the news the subject class. In our example, the subject class is the Employee class. The observers are the objects that are interested in getting the news. In our employee example, we have two observers: Payroll and TaxMan. When an object is interested in being informed of the state of the subject, it registers as an observer on that subject.

Conclusion


See also


Abstraction

References

[1] Gang of Four

[2] Christopher Alexander

[3] Observer Design pattern