A Brief Introductory into Design Patterns

This post is a brief overview of software design patterns from the Design Patterns in Ruby book. The book is a Ruby version of the original Gang of Four book- Design Patterns. We will be covering the following patterns in this post:

  • Template changes at a particular step
  • Strategy different algorithms to accomplish task
  • Observer classes watching another class for changes
  • Composite collection of objects that act as one
  • Iterator stores objects but allows access
  • Command wraps instructions in object
  • Adapter fixes interface mismatch
  • Proxy connects to networked object
  • Decorator adds additional behaviors to object
  • Singleton single instance everyone can use
  • Factory subclass produces objects
  • Builder builds complex objects
  • Interpreter customize interface to solve problem

Template

The template design pattern is useful when the application is prone to change at a given interval. While behaviors are initially defined in a base class (our "ReadHTMLFile" class), they can be inherited into a class that allows for greater customization.

class ReadHTMLFile
  ### omitted for brevity ###
  def html_version
    puts "<!DOCTYPE html>"
  end

  def read
    html_version
    @file.each_line {|line| puts line}
  end
end

class ReadHTML401Trans < ReadHTMLFile
  def html_version
    puts '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
  end
end

class ReadXHTML10Strict < ReadHTMLFile
  def html_version
    puts '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
  end
end

Our base class will read a HTML5 without any problems. But if we plan on reading some older XHTML1.0 Strict or HTML4.01 Trans files, we would instantiate one of the subclasses, which would overwrite our current #html_version with the correct doctype. So the Template strategy starts with a base class, and then its subclass will make the changes needed to suit to the given condition.

One of the drawbacks of this design pattern, is that it relies heavily on inheritance. The subclasses will always depend on its base class, and will limit our flexibility.

Strategy

The Strategy design pattern is based on composition and delegation rather than inheritance. It is where we pull the varying algorithm into a separate object, then pass it into the initializing subclass. The strategy class is what gets passed into the context.

class Accountant
  def work
    ### furiously presses numbers on keypad ###
  end
end

class Fireman
  def work
    ### saves kitten in tree ###
  end
end

class Person
  def initialize(job)
    @job = job
  end

  def preform_job
    @job.work
  end
end

Another way we can utilize the Strategy pattern is for the context class to pass its self into a strategy method. The example below also has commented out code for passing the strategy in as a proc. We would bundle up the workings of our #work method into a proc object and pass it in when initializing our Person class. Which ideally used when the strategy interface is a very simple design, such as our example below.

class Fireman
  def work(context)
    puts "Fireman #{context.name} climbs the tree to save kitten."
    ### additional information regarding Joe's life story ###
  end
end

class Person
  def initialize(strategy) # &strategy
    @name = 'Joe'
    @job = strategy
  end

  def preform_job
    @job.work(self)
    # @job.call(self)
  end
end

However, this increases the coupling between classes. And the context still needs a way to call the strategy, forcing us to still use an ivar for the strategy.

The overall major benefit of the strategy pattern is the separation of concerns, varying elements have been taken out and put into their own class.

Observer

If we have the potential that multiple other objects are interested in a particular class and its actions, they can register using the #add_observers() method. Otherwise, the below @observers array could be a single instantiation of an observing class that gets passed in upon the subjects initialization. The below example removes that implicit coupling as it is not dependant on whether none, one or many observer classes are in the array.

class Logger
  ### additional logging information omitted ###
  def update(obj)
    @log_file << "#{obj} is attempting to make a connection"
  end
end
class Connection
  def initialize
    @observers = [Logger.new]
  end
  def add_observer(obs)
    @observers << obs
  end
  def report_to_observers
    @observers.each {|observer| observer.update(self)}
  end
  def connect
    ### attempts to connect ###
    report_to_observers
  end
end

Brevity is key in our examples, but it would be best practice if we broke out the observer details into a module and included it in our Connection class.

Our @observer array could also store procs, changing the #add_observable(&observer) and observer.call(self) methods would accomplish this. Our examples use the pull technique, #update(self), is the act of sending the entire self object to the observer. The push technique send a great deal of additional information regarding the changes #update(self, old_salary, new_salary) or observer.update_salary(self, old_salary, new_salary).

The observer pattern is so common, Ruby also comes with its own observer module that can be found in the standard library.

Composite

Composite pattern is a combination of multiple smaller elements coming together to make one larger element. This forms a tree-like structure, the starting point at the top is the component, the very bottom of the tree are leaf classes, and the classes that fill the between are composite classes.

class BuildHouse
  def initialize
    @build_walls = BuildWalls.new
  end
end
  ### roof and other items have been omitted for brevity ###
class BuildWalls
  def initialize
    @sub_tasks = [LayFoundation.new, PurchaseLumber.new]
  end
  def num_of_tasks
    total = 1
    @sub_tasks.each {|task| total += task.num_of_tasks}
    total
  end
end
class LayFoundation
  def num_of_tasks
    total = 1
  end
end
class PurchaseLumber
  def num_of_tasks
    total = 1
  end
end

Our overall goal is to build houses. We use the composite pattern to create a house, but we break this up into smaller individual classes. In order to build a house, we need walls. Walls need a foundation on which to stand and the material of the walls needs to be purchased. BuildWalls is our composite class, its leaf classes are LayFoundation and PurchaseLumber. It needs these leaf classes in order to complete its own task. It is also being depended on by our component class, BuildHouse. All these classes work together to help BuildHouse accomplish its function. The only actual functionality we have in our example is asking how many tasks in total our classes have. Each leaf task is 1, if we ask our BuildWalls class about how many tasks it has, the answer would be 3 (including itself). In a real setting, these methods would get broken out into their own class/module such as Task and inherited or included, then written over as needed.

Iterator

Iterators pattern will provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

External

Is when the iterator is separate from the aggregate. The client drives the iteration, it will not call for the next element until it is ready for it. The external iterator will check if the array has an object available in the next field using #has_next?, then in the #next_item we pull the object out using the @index on the array, increment the @index by one, and use and implicit return with the value we initially set.

class ExternalIterator
  def initialize(array)
    @array = array
    @index = 0
  end
  def has_next?
    @index < @array.length
  end
  def item
    @array[@index]
  end
  def next_item
    value = @array[@index]
    @index += 1
    value
  end
end

i = ExternalIterator.new([3,4,5])
while i.has_next?
  puts i.next_item
end

Internal

With internal iterators, the aggregate will push the code block to accept item after item.

array.each {|e| **code block**}

Ruby iterators use the Enumerable module, more specifically, the <=> operator. The class you are iterating through needs some way for the iterator to compare values. If we were to create our own iterator, it would behoove us to include this mixin.

Command

The main idea of the Command pattern is to factor out the action code into its own object. It is the separation of concerns, from the code that does not change, to the code that does.

In our example below, we could have additional command classes. ShowCommand, HideCommand, SaveCommand, DeleteCommand, but the code that will not change with all these different commands is brought out into the UniveralButton class.

class ShowCommand
  ### @ivar will represent some sort of state ###
  def description
    "show " + @ivar
  end
  def execute
    ### Something will be shown ###
  end
end
class UniveralButton
  def initialize(command)
    @command = command
    @logger = File.open('/var/log/command_app.log')
  end
  def push_button
    @command.execute if @command
    @log.append @command.description
  end
end
button = UniversalButton.new(ShowCommand.new)

Using the command pattern also helps to log executed commands. Our ShowCommand is a class, so it should have some state information to add to our description, then it can be logged to a file for future use. Perhaps you do not want to execute the same command twice, a safeguarded code block may check our log file to see if the code has already been ran. Or could be used as an undo, knowing which command was executed last could help determine what to do in order to 'undo' that last function. The Madeleine gem is a great example of doing just that.

Adapter

An adapter is an object that crosses the chasm between the interface that you have and the interface that you need. Our below example has a EmployeeManager class, that would typically take an instantiated object of the Employee class. But we have a transfer from France, we would need a way for our French employee to interface with the employee manager. The FrenchEmployeeConverter will do just that, it will take our French employee and use its interface to correspond with the changes it needs to make to be of use to the EmployeeManager class.

class EmployeeManager
  def initialize
    @employee_list = []
  end
  ### Ommited different and fasinating ways to manage an employee ###
end
class Employee
  attr_reader :name, :position, :id
end
class FrenchEmployeeConverter
  def initialize(employe)
    @employe = employe
  end
  def name
    @employe.prenom
  end
  def position
    @employe.poste
  end
  def id
    @employe.ca
  end
end

Another easy way to do this, is to modify the class after its been instantiated, this will allow us to omit the adaptor class and use the original class as a regular Employee class.

employee = FrenchEmployee.new

class << employee
  def name
    prenom
  end
  ### additional changes to follow ###
end

This technique works well if the changes are simple, and we have deep knowledge of what our class is doing. However, if the changes are complex or we do not have a great understanding of what our class is doing, its safer to use FrenchEmployeeConverter class for our adaptor.

Proxy

Proxy classes hide away the real object within themselves, we refer to these real object as the "subject". This pattern is another separation of concerns. In our example, our employee data is hidden behind a EmployeeProxy that verifies the current user is authorized to view the sensitive data regarding a particular employee.

class Employee
  attr_reader :id, :name, :ssn
end
class EmployeeProxy
  def initialize(employee = Employee.new)
    @employee = employee
  end
  def name
    @employee.name
  end
  def ssn
    @employee.ssn if auth_user
  end
  def auth_user
    ### verifies permissions to view sensitive docs ###
  end
end

We can also use proxy classes for lazy loading. The employee subject does not get loaded until a method is called that needs to have the subject loaded.

class EmployeeProxy
  def initialize(&creation_block)
    @creation_block = creation_block
  end
  def subject
    @subject ||= @creation_block.call
  end
  def name
    e = subject
    e.name
  end
end

Instead of instantiating a Employee class directly, we will pass in a proc object and use that code block when needed EmployeeProxy.new(Employee.new). This lowers coupling and allows other ways of creating an employee subject, EmployeeProxy.new(Employee.find(#)).

Decorator

Our Decorator design pattern takes a Concrete Component(the "real" object) that implements the base functionality and passes it to our Decorator through a Component class. The Decorators act as specialty classes to give the base class additional functionality once instantiated. We are able to chain our decorators to give our super_tom object even more functionality.

class BaseEmployee ### ConcreteComponent ###
  def initialize(employee_hash)
    @name = employee_hash
  end
  ### base set of behaviors ###
  def work
    "work"
  end
end
class EnhancedEmployee ### Component ###
  def initialize(employee)
    @employee = employee
  end
  ### forward bevaviors to the base set ###
  def work
    @employee.work
  end
end
class Accountant < EnhancedEmployee ### Decorator ###
  def initialize(employee)
    super(employee)
  end
  def organizes_files
  end
end
class Developer < EnhancedEmployee ### Decorator ###
  def initialize(employee)
    super(employee)
  end
  def write_code 
  end
end
class DevOps < EnhancedEmployee ### Decorator ###
  def initialize(employee)
    super(employee)
  end
  def fix_server
  end
end
tom = Accountant.new( BaseEmployee.new(hash) )
super_tom = DevOps.new( Developer.new( BaseEmployee.new(hash) ) )

Another way to to get the decorator behaviors into out base class is to use modules. We would extend our instantiated class to include a Decorator if it has been defined as a module:

module Developer
  ### functionality here ###
end
tom = BaseEmployee.new
tom.extend(Developer)

Now our tom object will have the additional functionality from our Developer module.

Singleton

The purpose of a singleton pattern is to avoid passing an object all over the place. Its a method that is called directly on the class itself, instead of an instantiated object of the class. Each way is a valid approach to creating a class method.

class Network
  def self.terminate_all
  end
  def Network.terminate_all
  end
  class < self
    def terminate_all
    end
  end
end
def Network.terminate_all
end
Network.terminate_all

We could instantiate one object to pass around under our class methods. This object would be available anywhere the Logger class is available. However, we have to take precaution not to create additional objects with this type of pattern, there can be one, and only one instance of the singleton class. The private_class_method :new will ensure we do not instantiate any additional objects from this class.

class Logger
  @@instance = Logger.new
  def self.instance
    @@instance
  end
  private_class_method :new
end

The above code is so common, that Ruby has the singleton module for it. Just require 'singleton' and include Singleton inside our class and we would be able to omit the above mentioned code. The only difference is the module lazy instantiates our @@instance = Logger.new class variable, where our example eagerly instantiates it.

A singleton has a strong resemblance to the global variable, this give it the possibility to become tightly coupled with other sections of your program, there may also be difficulties locating where you have implemented the singleton when it could be in an arbitrary sections of code. The application of a singleton only works when you are modeling something that instantiates only once.

Testing a singleton pattern may be difficult due to its global variable behaviors, to fix this, we can break out our methods into a base class where we are able to instantiate and test our methods. And then have a separate class that inherits those methods for our implementation of our singleton.

class SimpleLogger
end
class SingletonLogger < SimpleLogger
  include Singeton
end

Factory

Factory patterns are very similar to the template patterns. It takes a base class and sends it to a creator class which will create multiple instance of the base/product class. The creator is the class that contains our factory methods, the products are the classes that we are creating. In our example, we create the Zoo class and define our common set of methods, then our specialized classes PenguinZoo will inherit these methods and add its own specialized methods.

class Zoo # creator
  def initialize(num, animal_type)
    @animals = []
    num.times { @animals << create_animal }
  end
end
class PenguinZoo < Zoo
  def create_animal
    Penguin.new
  end
end
class Penguin # product
end

We could go even further and create abstract factories. Where its the classes job to create compatable sets of objects.

class ArcticZooFactory # abstract factory
  def new_animal
    Penguin.new
  end
  def landscape
    Iceberg.new
  end
  def food
    Fish.new
  end
end
class Zoo
  def initialize(animal_num, landscape_num, exhibit_type)
    @exhibit_type = exhibit_type
    @animals = []
    animal_num.times { @animals << @exhibit_type.new_animal }
    ## create additional state from ArcticZooFactory ##
  end
end

This allows grouping of objects that have common properties. We could create other abstract factories such as SafariZooFactory, TropicalZooFactory, and DesertZooFactory. All would contain their respective classes.

Builder

A builder class takes charge of assembling all of the components of a complex object. Its purpose is to ease the burden of creating complex objects. We no longer have to know the specifics of a certain class, we just ask the builder for what we need. Builders are less concerned with picking the right class and more focued on helping you configure your object. Our below builder has a #reset method that allows us to reuse the builder.

class Shell
end
class Cheese
  def initialize(type)
    @type = type
  end
end
class Meat
  def initialize(type)
    @type = type
  end
end
class Taco
  def cheese_layer(cheese)
    @cheese << cheese
  end
end
class TacoBuilder
  attr_reader :taco
  def initialize
    @taco = Taco.new
  end
  def reset
    @taco = Taco.new
  end
  def add_shell
  end
  def add_meat(type="beef")
    @taco.meat = Meat.new(type)
  end
  def add_cheese(type)
    @taco.cheese_layer << Cheese.new(type)
  end
  def add_salsa(spicy=true)
  end
  def taco
    raise "no shell" if @taco.shell.nil? == true
  end
end

Our builder class will allow us to use constraints when we attempt to retrieve our taco object. In our example above, we will throw an error if there is no shell on hour taco. Also, notice the #reset method, this allows us to reused the builder after creating an instance of Taco.

Interpreter

A parser reads in some program text and produces a data structure called abstract syntax tree. The abstract syntax tree(AST) has terminals and nonterminals, much like leaf and a parent. These nonterminals extend to the terminal leaf nodes.

class All
  ### retrieves everything ###
end
class Not
  def initialize(expression)
    @expression = expression
  end
  def evaluate
    All.new - @expression
  end
end
class Writable
  ### retrieves only writable objects ###
end
writable = Writable.new.evaluate
read_only = Not.new( Writable.new ).evaluate

Typically we would use the All class to find all our objects, or Writable to just find the writable files, but now with have a nonterminal Not class that we could use to negate whatever we pass into it as an argment. The Not class will use two terminal classes, our All and Writable to remove all writable objects from the all group. This is a brief example, but we could also include other classes such as Or and And. These nonterminals could use other combinations of terminal classes such as Readable and Executable, giving us a larger tress structure that we could traverse.

Another way to create a AST is to build a parser. We could define our syntax to look like all except writable, our class would parse through out command, taking all and setting a base set of files to work with, and our except would negate those file against ones that are writable.

class Parser
  def expression
    case next_token
    when 'writable'
      Writable.new
    when 'all'
      All.new
    when 'except'
      Except.new(next_token)
    end
  end
end
all except writable
class Expression
  def |(other)
    Or.new(self, other)
  end
  def &(other)
    And.new(self, other)
  end
  def writable
    Writable.new
  end
  def but_not(arg) # avoiding collision with Rubys 'not' operator
    Not.new(arg)
  end
  ### We can use this for all our classes ###
end
( writable & but_not(executable) ) | find_filename("*.txt")