You are here:   ArielOrtiz.com > Software Design and Architecture > Lab 4: Iterator and Command Patterns

Lab 4: Iterator and Command Patterns

Objectives

During this lab session:

This activity helps students develop the following skills, values and attitudes: ability to analyze and synthesize, capacity for identifying and solving problems, and efficient use of computer systems.

Activity Description

The lab activities can be developed individually or in pairs.

The lab report must be developed individually.

  1. Create a folder called iterator_command. Inside this folder, create four files called: primes.rb, test_primes.rb, control.rb, and test_control.rb.

    All four Ruby source files must start with a comment containing the lab's title, date, and the authors' personal information. For example:

    # Lab 4: Iterator and Command Patterns
    # Date: 15-Feb-2011
    # Authors:
    # 456654  Anthony Stark 
    # 1160611 Thursday Rubinstein
  2. In the primes.rb source file, write a class called PrimeGenerator. Instances of this class can be used as internal or external iterators that produce all prime numbers contained within a lower and upper limit inclusively.

    The class must have the following methods: initialize, next_item, item, has_next?, and each, as explained in chapter 7 of [OLSEN].

  3. The following unit tests verify the correct behavior of the PrimeGenerator class. Place the test class in the test_primes.rb source file.

    # File: test_primes.rb
    
    require 'test/unit'
    require './primes'
    
    class PrimeGeneratorTest < Test::Unit::TestCase
    
      def setup
        @pg = PrimeGenerator.new(31, 97)
        @expected_values = [31, 37, 41, 43, 47, 
                            53, 59, 61, 67, 71, 
                            73, 79, 83, 89, 97]
      end
    
      def test_external_iterator    
        @expected_values.each do |n|
          assert @pg.has_next?
          assert_equal n, @pg.next_item
          assert_equal n, @pg.item  
        end
        assert ! @pg.has_next?
      end
      
      def test_internal_iterator
        i = 0
        @pg.each do |p|
          assert_equal @expected_values[i], p
          i += 1
        end
      end
      
    end
  4. This example was taken from [FREEMAN] pp. 191-223. You are required to write part of the software for the control remote shown in the following image, using the Command pattern.

    A remote control

    You are given the following Ruby code (assume it is placed in the control.rb source file):

    # File: control.rb
    
    class RemoteControlWithUndo
      
      def initialize
        @on_commands = []
        @off_commands = []     
        no_command = NoCommand.new
        7.times do
          @on_commands << no_command
          @off_commands << no_command
        end  
        @undo_command = no_command
      end
          
      def set_command(slot, on_command, off_command)
        @on_commands[slot] = on_command
        @off_commands[slot] = off_command
      end
     
      def on_button_was_pushed(slot)
        @on_commands[slot].execute
        @undo_command = @on_commands[slot]
      end
     
      def off_button_was_pushed(slot)
        @off_commands[slot].execute
        @undo_command = @off_commands[slot]
      end
     
      def undo_button_was_pushed() 
        @undo_command.undo
      end
      
      def inspect    
        string_buff = ["\n------ Remote Control -------\n"]
        @on_commands.zip(@off_commands).each_with_index do |commands, i| 
          on_command, off_command = commands
          string_buff << "[slot #{i}] #{on_command.class}  #{off_command.class}\n"          
        end
        string_buff << "[undo] #{@undo_command.class}\n"         
        string_buff.join
      end  
      
    end
    
    class NoCommand
    
      def execute
      end
      
      def undo
      end
      
    end
    
    class Light   
      
      attr_reader :level
    
      def initialize(location)
        @location = location
        @level = 0
      end
    
      def on
        @level = 100
        puts "Light is on"
      end
    
      def off
        @level = 0
        puts "Light is off"
      end
    
      def dim(level)
        @level = level
        if level == 0
          off
        else 
          puts "Light is dimmed to #{@level}%"
        end
      end  
      
    end
    
    class CeilingFan
    
      HIGH   = 3
      MEDIUM = 2
      LOW    = 1
      OFF    = 0
      
      attr_reader :speed
        
      def initialize (location)
        @location = location
        @speed = OFF
      end
      
      def high
        @speed = HIGH;
        puts "#{@location} ceiling fan is on high"
      end
     
      def medium
        @speed = MEDIUM
        puts "#{@location} ceiling fan is on medium"
      end
     
      def low
        @speed = LOW
        puts "#{@location} ceiling fan is on low"
      end
      
      def off
        @speed = OFF
        puts "#{@location} ceiling fan is off"
      end  
      
    end
    

    Write the following command classes placing them in the control.rb source file: LightOnCommand, LightOffCommand, CeilingFanHighCommand, CeilingFanMediumCommand, and CeilingFanOffCommand. All these classes should have these three methods: initialize, execute and undo. Check the unit tests in the next step to understand the expected behavior of each method.

  5. The following unit tests verify the correct behavior of your classes. Place the test class in the test_control.rb source file.

    # File: test_control.rb
    
    require 'test/unit'
    require 'stringio'
    require './control'
    
    class ControlTest < Test::Unit::TestCase
        
      def setup
        set_stdout    
        @rc = RemoteControlWithUndo.new
        set_light
        set_fan                     
      end
    
      def teardown
        reset_stdout
      end
      
      def set_stdout
        @out = StringIO.new
        @old_stdout = $stdout
        $stdout = @out
      end
      
      def reset_stdout
        $stdout = @old_stdout
      end
      
      def set_light
        light = Light.new("Living Room")
        light_on = LightOnCommand.new(light)
        light_off = LightOffCommand.new(light)    
        @rc.set_command(0, light_on, light_off)
      end
      
      def set_fan
        fan = CeilingFan.new("Living Room")
        fan_medium = CeilingFanMediumCommand.new(fan)
        fan_high = CeilingFanHighCommand.new(fan)
        fan_off = CeilingFanOffCommand.new(fan)     
        @rc.set_command(1, fan_medium, fan_off)
        @rc.set_command(2, fan_high, fan_off)
      end
    
      def test_light
        @rc.on_button_was_pushed(0)
        @rc.off_button_was_pushed(0)
        p @rc
        @rc.undo_button_was_pushed
        @rc.off_button_was_pushed(0)
        @rc.on_button_was_pushed(0)
        p @rc
        @rc.undo_button_was_pushed
        assert_equal "Light is on\n"                                             \
                     "Light is off\n"                                            \
                     "\n------ Remote Control -------\n"                         \
                     "[slot 0] LightOnCommand  LightOffCommand\n"                \
                     "[slot 1] CeilingFanMediumCommand  CeilingFanOffCommand\n"  \
                     "[slot 2] CeilingFanHighCommand  CeilingFanOffCommand\n"    \
                     "[slot 3] NoCommand  NoCommand\n"                           \
                     "[slot 4] NoCommand  NoCommand\n"                           \
                     "[slot 5] NoCommand  NoCommand\n"                           \
                     "[slot 6] NoCommand  NoCommand\n"                           \
                     "[undo] LightOffCommand\n\n"                                \
                     "Light is on\n"                                             \
                     "Light is off\n"                                            \
                     "Light is on\n"                                             \
                     "\n------ Remote Control -------\n"                         \
                     "[slot 0] LightOnCommand  LightOffCommand\n"                \
                     "[slot 1] CeilingFanMediumCommand  CeilingFanOffCommand\n"  \
                     "[slot 2] CeilingFanHighCommand  CeilingFanOffCommand\n"    \
                     "[slot 3] NoCommand  NoCommand\n"                           \
                     "[slot 4] NoCommand  NoCommand\n"                           \
                     "[slot 5] NoCommand  NoCommand\n"                           \
                     "[slot 6] NoCommand  NoCommand\n"                           \
                     "[undo] LightOnCommand\n\n"                                 \
                     "Light is off\n", @out.string
      end
      
      def test_fan
        @rc.on_button_was_pushed(1)
        @rc.off_button_was_pushed(1)
        p @rc
        @rc.undo_button_was_pushed
        @rc.on_button_was_pushed(2)
        p @rc
        @rc.undo_button_was_pushed
        assert_equal "Living Room ceiling fan is on medium\n"                   \
                     "Living Room ceiling fan is off\n"                         \
                     "\n------ Remote Control -------\n"                        \
                     "[slot 0] LightOnCommand  LightOffCommand\n"               \
                     "[slot 1] CeilingFanMediumCommand  CeilingFanOffCommand\n" \
                     "[slot 2] CeilingFanHighCommand  CeilingFanOffCommand\n"   \
                     "[slot 3] NoCommand  NoCommand\n"                          \
                     "[slot 4] NoCommand  NoCommand\n"                          \
                     "[slot 5] NoCommand  NoCommand\n"                          \
                     "[slot 6] NoCommand  NoCommand\n"                          \
                     "[undo] CeilingFanOffCommand\n\n"                          \
                     "Living Room ceiling fan is on medium\n"                   \
                     "Living Room ceiling fan is on high\n"                     \
                     "\n------ Remote Control -------\n"                        \
                     "[slot 0] LightOnCommand  LightOffCommand\n"               \
                     "[slot 1] CeilingFanMediumCommand  CeilingFanOffCommand\n" \
                     "[slot 2] CeilingFanHighCommand  CeilingFanOffCommand\n"   \
                     "[slot 3] NoCommand  NoCommand\n"                          \
                     "[slot 4] NoCommand  NoCommand\n"                          \
                     "[slot 5] NoCommand  NoCommand\n"                          \
                     "[slot 6] NoCommand  NoCommand\n"                          \
                     "[undo] CeilingFanHighCommand\n\n"                         \
                     "Living Room ceiling fan is on medium\n", @out.string
      end
    end

Deliverables

To hand in your individual lab work, follow these instructions.

Due date is Monday, February 21.

Evaluation

This activity will be evaluated using the following criteria:

50% Implementation of functional requirements.
50% Lab report.
DA The program and/or report was plagiarized.
© 1996-2011 by Ariel Ortiz (ariel.ortiz@itesm.mx)
Made with Django | Licensed under Creative Commons | Valid XHTML | Valid CSS