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

Lab 4: Command and Adapter Patterns

Objectives

During this lab session:

This activity helps the student 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

This lab is based on chapters 6 and 7 of [FREEMAN]. It can be developed individually or in pairs.

The following ZIP file contains the files and folders you'll be needing for this lab: command_adapter.zip. Any new files you create must go inside the corresponding folder.

  1. We have the following source code for a blender class:

    namespace Headfirst.Command.Undo {
    
        public class Blender {
        
            int speed = 0;
        
            public int CurrentSpeed() {            
                return speed;
            }
            
            public string NewSpeed(int speed) {
            
                if (0 <= speed && speed <= 10) {
                    this.speed = speed;
                }
                
                if (speed == 0) {
                    return "Blender OFF";
                    
                } else {
                    return string.Format("Blender ON speed = {0}", speed);
                }
            }
        }
    }

    We want to be able to use instances of this class through a remote control with undoable capabilities. In order to do this, you must write the following new classes:

    • Headfirst.Command.Undo.BlenderIncreaseSpeedCommand
    • Headfirst.Command.Undo.BlenderOffCommand

    Make sure that they behave exactly as expected by the following unit test. Do not modify any other part of the code.

    namespace Headfirst.Command.Undo {
        
        using System;
        using NUnit.Framework;
        
        [TestFixture]
        public class TestCommand {
            
            static readonly string EOL = Environment.NewLine;       
            
            [Test]
            public void TestBlender() {
                
                var remoteControl = new RemoteControlWithUndo();
                var blender = new Blender();
                var increaseCommand = new BlenderIncreaseSpeedCommand(blender);
                var offCommand = new BlenderOffCommand(blender);
        
                remoteControl.SetCommand(0, increaseCommand, offCommand);
        
                Assert.AreEqual("Blender ON speed = 1", 
                    remoteControl.OnButtonWasPushed(0));
                Assert.AreEqual("Blender OFF",
                    remoteControl.OffButtonWasPushed(0));
                Assert.AreEqual("Blender ON speed = 1", 
                    remoteControl.UndoButtonWasPushed());            
                Assert.AreEqual("Blender ON speed = 2",
                    remoteControl.OnButtonWasPushed(0));
                Assert.AreEqual(
                    "------ Remote Control -------" + EOL
                    + "[slot 0] BlenderIncreaseSpeedCommand BlenderOffCommand" + EOL
                    + "[slot 1] NoCommand NoCommand" + EOL
                    + "[slot 2] NoCommand NoCommand" + EOL
                    + "[slot 3] NoCommand NoCommand" + EOL
                    + "[slot 4] NoCommand NoCommand" + EOL
                    + "[slot 5] NoCommand NoCommand" + EOL
                    + "[slot 6] NoCommand NoCommand" + EOL
                    + "[undo] BlenderIncreaseSpeedCommand" + EOL, 
                    remoteControl.ToString());
                Assert.AreEqual("Blender ON speed = 3", 
                    remoteControl.OnButtonWasPushed(0));                
                Assert.AreEqual("Blender ON speed = 4", 
                    remoteControl.OnButtonWasPushed(0));
                Assert.AreEqual("Blender ON speed = 3", 
                    remoteControl.UndoButtonWasPushed());
                Assert.AreEqual("Blender ON speed = 4", 
                    remoteControl.OnButtonWasPushed(0));
                Assert.AreEqual("Blender ON speed = 5",
                    remoteControl.OnButtonWasPushed(0));
                Assert.AreEqual("Blender ON speed = 6", 
                    remoteControl.OnButtonWasPushed(0));
                Assert.AreEqual("Blender OFF", 
                    remoteControl.OffButtonWasPushed(0));
                Assert.AreEqual("Blender ON speed = 6",
                    remoteControl.UndoButtonWasPushed());            
                Assert.AreEqual(
                    "------ Remote Control -------" + EOL
                    + "[slot 0] BlenderIncreaseSpeedCommand BlenderOffCommand" + EOL
                    + "[slot 1] NoCommand NoCommand" + EOL
                    + "[slot 2] NoCommand NoCommand" + EOL
                    + "[slot 3] NoCommand NoCommand" + EOL
                    + "[slot 4] NoCommand NoCommand" + EOL
                    + "[slot 5] NoCommand NoCommand" + EOL
                    + "[slot 6] NoCommand NoCommand" + EOL
                    + "[undo] BlenderOffCommand" + EOL, 
                    remoteControl.ToString());                
            }
        }    
    }
  2. The ZIP file contains inside the chapter07 folder a dynamic link library called Stack.dll which includes the definition of the Headfirst.Adapter.Stack<T> generic class. A stack is a data structure that follows a LIFO (last in, first out) policy. It's public interface is described in the following table:

    Name Description
    bool IsEmpty Property that returns true if the current stack instance is empty, or false otherwise.
    int Count Property that returns the number of elements actually stored in the current stack instance.
    void Push(T value) Method that inserts value at the top of the current stack instance.
    T Pop() Method that removes and returns the element at the top of the current stack instance. Throws a System.InvalidOperationException if called on an empty stack.

    You are also given the following interface definition for a generic queue type:

    namespace Headfirst.Adapter {
        
        public interface IQueue<T> {
            bool IsEmpty { get; }
            int Count { get; }
            void Insert(T value);
            T Remove();        
        }
    }

    A queue is a data structure that follows a FIFO (first in, first out) policy. The Insert method adds an element at the end of the current queue. Remove removes and returns the element at the front of the current queue, or throws an exception if the queue is empty. The IsEmpty and Count properties work like their stack counterparts.

    Write a class called Headfirst.Adapter.StackishQueue<T> that adapts a stack so that it can be used as a queue. Make sure it behaves exactly as expected by the following unit tests.

    namespace Headfirst.Adapter {
        
        using System;
        using NUnit.Framework;
        
        [TestFixture]
        public class TestAdapter {
            
            [Test]
            public void TestStackishQueueOfInts() {
                
                Stack<int> s = new Stack<int>();
                IQueue<int> q = new StackishQueue<int>(s);
                
                Assert.IsTrue(s.IsEmpty);
                Assert.IsTrue(q.IsEmpty);
                q.Insert(0);
                q.Insert(1);
                q.Insert(2);
                q.Insert(3);
                Assert.IsFalse(s.IsEmpty);
                Assert.IsFalse(q.IsEmpty);
                Assert.AreEqual(4, s.Count);
                Assert.AreEqual(4, q.Count);
                Assert.AreEqual(0, q.Remove());
                Assert.AreEqual(1, q.Remove());
                Assert.AreEqual(2, q.Remove());
                Assert.AreEqual(3, q.Remove());
                Assert.IsTrue(s.IsEmpty);
                Assert.IsTrue(q.IsEmpty);   
                Assert.AreEqual(0, s.Count);
                Assert.AreEqual(0, q.Count);
                try {
                    q.Remove();
                    Assert.Fail();
                } catch (InvalidOperationException e) {
                    Assert.AreEqual("You can't remove from an empty queue!", 
                        e.Message);                
                }
            }
            
            [Test]
            public void TestStackishQueueOfStrings() {
                
                Stack<string> s = new Stack<string>();
                IQueue<string> q = new StackishQueue<string>(s);
                
                Assert.IsTrue(s.IsEmpty);
                Assert.IsTrue(q.IsEmpty);
                q.Insert("Frodo");
                q.Insert("Gandalf");
                q.Insert("Legolas");            
                Assert.IsFalse(s.IsEmpty);
                Assert.IsFalse(q.IsEmpty);
                Assert.AreEqual(3, s.Count);
                Assert.AreEqual(3, q.Count);
                Assert.AreEqual("Frodo", q.Remove());
                Assert.AreEqual("Gandalf", q.Remove());
                Assert.AreEqual(1, s.Count);
                Assert.AreEqual(1, q.Count);
                Assert.AreEqual("Legolas", q.Remove());            
                Assert.IsTrue(s.IsEmpty);
                Assert.IsTrue(q.IsEmpty);   
                Assert.AreEqual(0, s.Count);
                Assert.AreEqual(0, q.Count);
                try {
                    q.Remove();
                    Assert.Fail();
                } catch (InvalidOperationException e) {
                    Assert.AreEqual("You can't remove from an empty queue!", 
                        e.Message);                
                }
            }
        }
    }

Deliverables

To hand in your lab work, follow these instructions:

Evaluation

This activity will be evaluated using the following criteria:

100 The code works as requested.
60-90 The code works, but has some flaws.
20-50 The code doesn't work, but it seams that some amount of time was spent on it.
DA The program was plagiarized.
© 1996-2009 by Ariel Ortiz (ariel.ortiz@itesm.mx)
Made with Django | Licensed under Creative Commons | Valid XHTML | Valid CSS