From my studies of graduate students in the Masters of Software Engineering program at Carroll University, I think that most object-oriented programmers are procedural programmers using an object-oriented language. A more detailed discussion of this is found here. Are you one of these programmers? The following five exercises can be solved without using any conditional logic. If you can't solve these problems, study them to expand your scope and add some tools to your toolbox. Each problem has working code for an unsolved example (uses conditional logic) and a solved example (no conditional logic). Study these examples to expand your capabilities. Then, you will have more than a hammer in your toolbox and not every problem will appear as a nail. Having these tools will help you build more complex "Enterprise" systems without creating BBOM's (Big Balls of Mud). As always, stick to the adage, "PROGRAM TO THE INTERFACE AND NOT TO THE IMPLEMENTATION."
Exercise I: Bergin Swing Calculator (Solution: Strategy Design Pattern)
Exercise II: Doktat's Stopwatch (Solution: State Design Pattern)
Exercise III: Dependency Inversion Principle
Exercise IV: Doktat's Elevator (Solution: Bridge Design Pattern)
Exercise V: Doktat's Pizza Place (Solution: Decorator Design Pattern & Reflection)
Monday, July 13, 2009
Saturday, March 21, 2009
Challenge Five Answer: Decorator Design Pattern and Reflection
One approach to this problem is to have a class representing each of the possible pizza types and extra toppings. For example, one class might be ThinCrustOnionPepperoni whereas another might be ChicagoStyleAnchovy. Thus, if the user selects the Thin Crust radio button and Onion check box, an instance of ThinCrustOnion would be instantiated and used for the source of data needed to display the description and the cost. This is a brittle solution because it is very difficult to add and subtract components from the menu. New classes with all the various combinations of components must be pre-constructed and this type of class definition isn't necessary when we can build the class dynamically. The Decorator Design Pattern is ideally suited for this kind of situation because it permits us to add responsibilities to our constructed object dynamically. The code discussed in the following can be downloaded here.
The GOF text speaks of the Decorator Design Pattern as a "degenerate composite with only one component." Java programmers encounter this design pattern when they begin using classes in the java.io package in a manner similar to this:
DataInputStream dis = new DataInputStream(new FileInputStream(new File("name")));.
All of the classes referenced in the preceding statement are subclasses of java.io.InputStream and each one has a constructor that takes an InputStream argument. How can this practice of dynamically building responsibilities into the desired class be employed in our pizza place?
Consider the following statement as an alternative to our previous solution where pre-constructed classes represented the range of pizza types and toppings.
PizzaComponent pizza = new Onion(new Pepperoni(new ThinCrust()));
An application employing this type of solution to our problem is easier to change. All that is required is to add or omit the relevant component and change the GUI. An application using this approach is available here.
However, since this discussion appears in the context of programming without if's, we still have more progress to make. Our application is still using conditional logic to determine which combination of radio button and check boxes was selected and then subsequently builds the desired pizza. Wouldn't it be nice to have a way to build a list of the desired components and then when the user proceeds to the checkout, the list of components is used to dynamically build our pizza object? Then it wouldn't be necessary to use any conditional logic to determine which components were to be included as decorators on our pizza. We can't build a list of object instances because the components are built from instances of each other. Can our list be composed of the class names? Yes - and we can use reflection combined with the Decorator to build our pizza without the use of conditional logic.
The following discussion introduces us to another GOF design pattern, the Builder Design Pattern, but we will save the discussion of Builder for another time. In the application available here a few new features have been introduced to obviate the need for conditional logic. Subclasses of JRadioButton and JCheckBox, SelfListeningJRadioButton and SelfListeningJCheckBox, respectively, were created to relieve the GUI from having to determine (with conditional logic) which button or box was selected. In other words we are now giving each button or checkbox the responsibility of listening to itself and also knowing what to do when it is selected. Subclasses of these self-listening components, such as OnionCheckBox are instantiated and passed a reference to an instance of PizzaComponentBuilder which is used to store the list of class names of the components selected by the user. In other words, each time a user selects a radio button or a checkbox the class name of that pizza component is added to a List on the builder object.
There are more features included in this application that need to be discussed. To deal with users changing their minds while selecting components, the capability to unselect a checkbox was included. Since selecting a checkbox results in adding the class name to the list in the builder, de-selecting a checkbox must remove the class name from the list. This capability was added with the use of the Strategy Design Pattern (discussed already in Challenge One: The Bergin Calculator application) and accounts for the 'on' and 'off' strategies associated with each of the components. With the use of these strategies the checkbox components do not need to ask themselves what state they are in to perform their responsibilities.
Because the method we are using in this application is much easier to implement, we have included the capability to select one of the other pizza types, ie., Chicago Style, Pan or M'Waukee Style. Again, the Strategy Design Pattern was used to obviate the need to use conditional logic to determine which of the pizza types was to serve as the base component in the construction of the pizza.
Now we consider the way the PizzaComponentBuilder constructs the pizza object from the List containing the class names of the components. As mentioned above we combine the use of reflection with the Decorator Design Pattern to build our pizza object. Consider the method shown below which contains all that is needed. The strComponents object is a List which contains the class names of the components as Strings.
public PizzaComponent buildComponentUsingReflection()
{
newComponent = currentStrategy.getBaseComponent();
Object[] arg = { newComponent };
iterator = strComponents.iterator();
while (iterator.hasNext())
{
try
{
someClass = Class.forName((String) iterator.next());
constructors = someClass.getConstructors();
newComponent = (PizzaComponent) constructors[0].newInstance(arg);
}
catch (Exception ex3)
{
System.out.println("something wrong in PizzaComponentBuilder 3 " + ex3);
}
arg[0] = newComponent;
}
strComponents.clear();
return newComponent;
}
The first statement from the method above determines which pizza type was selected, ie., Thin Crust, Pan, etc., and the second step assigns to the object array the inner most (nucleus) component of our pizza object. The iteration is started that goes through the list of class names that will be the components used to build the pizza object. The first statement in the try-catch block creates an instance of Class that represents the class identified by the class name (String) in the array. The first time through the iteration finds this class to be one of the pizza types which was placed in the array in the above-described steps. The next step obtains the array of Constructors (Constructors[])which was defined with class scope and not shown in the method (see code package). The next step containing the invocation of contructors[0].newInstance(PizzaComponent) is not immediately obvious. This is the step that instantiates the class represented in the first step, which will be the pizza type the user selected. For purposes of discussion, assume the type is Thin Crust and therefore the object will be an instance of ThinCrust class. The signature of Contructor.newInstance requires the arguments to the constructor to be passed as an array containing the argument objects. In our case the array argument, 'arg' contains only one component, the instance of ThinCrust set in the second statement of the method. Then, once the new PizzaComponent is instantiated and we leave the try-catch block, we assign to the 'arg' array the object that was just instantiated. On the subsequent iteration, this is the argument used in the constructor for the next level of our decorated pizza object.
The GOF text speaks of the Decorator Design Pattern as a "degenerate composite with only one component." Java programmers encounter this design pattern when they begin using classes in the java.io package in a manner similar to this:
DataInputStream dis = new DataInputStream(new FileInputStream(new File("name")));.
All of the classes referenced in the preceding statement are subclasses of java.io.InputStream and each one has a constructor that takes an InputStream argument. How can this practice of dynamically building responsibilities into the desired class be employed in our pizza place?
Consider the following statement as an alternative to our previous solution where pre-constructed classes represented the range of pizza types and toppings.
PizzaComponent pizza = new Onion(new Pepperoni(new ThinCrust()));
An application employing this type of solution to our problem is easier to change. All that is required is to add or omit the relevant component and change the GUI. An application using this approach is available here.
However, since this discussion appears in the context of programming without if's, we still have more progress to make. Our application is still using conditional logic to determine which combination of radio button and check boxes was selected and then subsequently builds the desired pizza. Wouldn't it be nice to have a way to build a list of the desired components and then when the user proceeds to the checkout, the list of components is used to dynamically build our pizza object? Then it wouldn't be necessary to use any conditional logic to determine which components were to be included as decorators on our pizza. We can't build a list of object instances because the components are built from instances of each other. Can our list be composed of the class names? Yes - and we can use reflection combined with the Decorator to build our pizza without the use of conditional logic.
The following discussion introduces us to another GOF design pattern, the Builder Design Pattern, but we will save the discussion of Builder for another time. In the application available here a few new features have been introduced to obviate the need for conditional logic. Subclasses of JRadioButton and JCheckBox, SelfListeningJRadioButton and SelfListeningJCheckBox, respectively, were created to relieve the GUI from having to determine (with conditional logic) which button or box was selected. In other words we are now giving each button or checkbox the responsibility of listening to itself and also knowing what to do when it is selected. Subclasses of these self-listening components, such as OnionCheckBox are instantiated and passed a reference to an instance of PizzaComponentBuilder which is used to store the list of class names of the components selected by the user. In other words, each time a user selects a radio button or a checkbox the class name of that pizza component is added to a List
There are more features included in this application that need to be discussed. To deal with users changing their minds while selecting components, the capability to unselect a checkbox was included. Since selecting a checkbox results in adding the class name to the list in the builder, de-selecting a checkbox must remove the class name from the list. This capability was added with the use of the Strategy Design Pattern (discussed already in Challenge One: The Bergin Calculator application) and accounts for the 'on' and 'off' strategies associated with each of the components. With the use of these strategies the checkbox components do not need to ask themselves what state they are in to perform their responsibilities.
Because the method we are using in this application is much easier to implement, we have included the capability to select one of the other pizza types, ie., Chicago Style, Pan or M'Waukee Style. Again, the Strategy Design Pattern was used to obviate the need to use conditional logic to determine which of the pizza types was to serve as the base component in the construction of the pizza.
Now we consider the way the PizzaComponentBuilder constructs the pizza object from the List
public PizzaComponent buildComponentUsingReflection()
{
newComponent = currentStrategy.getBaseComponent();
Object[] arg = { newComponent };
iterator = strComponents.iterator();
while (iterator.hasNext())
{
try
{
someClass = Class.forName((String) iterator.next());
constructors = someClass.getConstructors();
newComponent = (PizzaComponent) constructors[0].newInstance(arg);
}
catch (Exception ex3)
{
System.out.println("something wrong in PizzaComponentBuilder 3 " + ex3);
}
arg[0] = newComponent;
}
strComponents.clear();
return newComponent;
}
The first statement from the method above determines which pizza type was selected, ie., Thin Crust, Pan, etc., and the second step assigns to the object array the inner most (nucleus) component of our pizza object. The iteration is started that goes through the list of class names that will be the components used to build the pizza object. The first statement in the try-catch block creates an instance of Class that represents the class identified by the class name (String) in the array. The first time through the iteration finds this class to be one of the pizza types which was placed in the array in the above-described steps. The next step obtains the array of Constructors (Constructors[])which was defined with class scope and not shown in the method (see code package). The next step containing the invocation of contructors[0].newInstance(PizzaComponent) is not immediately obvious. This is the step that instantiates the class represented in the first step, which will be the pizza type the user selected. For purposes of discussion, assume the type is Thin Crust and therefore the object will be an instance of ThinCrust class. The signature of Contructor.newInstance requires the arguments to the constructor to be passed as an array containing the argument objects. In our case the array argument, 'arg' contains only one component, the instance of ThinCrust set in the second statement of the method. Then, once the new PizzaComponent is instantiated and we leave the try-catch block, we assign to the 'arg' array the object that was just instantiated. On the subsequent iteration, this is the argument used in the constructor for the next level of our decorated pizza object.
Friday, March 20, 2009
Challenge Five: Doktat's Pizza Place
As shown in the figure below the pizza problem presents the developer with many choices of components that combine to form the object of interest. This is an ideal situation for implementation of the Decorator Design Pattern and yes, this problem can be solved without the use of any conditional logic. See if you can do it - the code for the GUI is here.
The user selects the type of pizza from the radio buttons on the left and with the use of the check boxes on the right, indicates the desired toppings. In the schematic below, the user has selected a "Chicago Style" pizza with onion, pepperoni and green pepper toppings but the "CHECK OUT" button hasn't been clicked yet.
When it's time to check out, the user clicks on the "CHECK OUT" button and the text field below the button presents the user with the style of pizza and toppings included in the order. The text field below that shows the cost. The cost was determined by summing the costs of the individual
components used to create the pizza object which was: Chicago Style Pizza = $16.50; Onion = $1.15; Pepperoni = $1.25; Green Pepper = $1.20. Another requirement of the application is that after the "CHECK OUT" button has been clicked, the check boxes and radio buttons have been cleared and are ready for another pizza selection. Also, the user must be able to change their mind about toppings before the "CHECK OUT" button has been clicked. For example, if the user had already selected the "Anchovy" check box and then decided to delete the Anchovy selection, another click on the "Anchovy" check box will toggle off the selection.
A solution to Programming Without Ifs: Challenge Five will be discussed in a subsequent blog.
The user selects the type of pizza from the radio buttons on the left and with the use of the check boxes on the right, indicates the desired toppings. In the schematic below, the user has selected a "Chicago Style" pizza with onion, pepperoni and green pepper toppings but the "CHECK OUT" button hasn't been clicked yet.
When it's time to check out, the user clicks on the "CHECK OUT" button and the text field below the button presents the user with the style of pizza and toppings included in the order. The text field below that shows the cost. The cost was determined by summing the costs of the individual
components used to create the pizza object which was: Chicago Style Pizza = $16.50; Onion = $1.15; Pepperoni = $1.25; Green Pepper = $1.20. Another requirement of the application is that after the "CHECK OUT" button has been clicked, the check boxes and radio buttons have been cleared and are ready for another pizza selection. Also, the user must be able to change their mind about toppings before the "CHECK OUT" button has been clicked. For example, if the user had already selected the "Anchovy" check box and then decided to delete the Anchovy selection, another click on the "Anchovy" check box will toggle off the selection.
A solution to Programming Without Ifs: Challenge Five will be discussed in a subsequent blog.
Tuesday, March 10, 2009
Challenge Four: Answer - Bridge Design Pattern
Challenge Four
The first line of thinking about this problem was similar to that of the stopwatch. The elevator can be in one of three states: top, middle, or bottom corresponding to the third floor, second floor and first floor respectively. But our state diagram takes on extra states due to more than one behavior possible for a particular state. For example, the elevator in the top state can go either to the middle state (second floor) or to the bottom state (first floor). Thus, just like the stopwatch, we model the states of the elevator depending on what the user does.
The second degree of freedom in this application is the behavior of a "called" elevator (the user pushes the ""CALL" button) verses the behavior of the elevator when the user "takes" the elevator (uses one of the "GO" buttons). When a user "takes" the elevator to a floor, the textfield announcement is that the elevator arrives, the door opens and the user exits. When a user "calls" the elevator (uses one of the "CALL" buttons), the textfield announcement is that the elevator arrives and the door opens. Thus, a response to a "CALL" button cannot be implemented as though it were a response to a "GO" button. Another part of the problem is that there are two "GO" buttons on each floor. This makes it relatively easy to assign to a listener what state should handle a click on a "GO" button.
This UML class diagram does not show the classes involved in the GUI aspects of the application. This UML class diagram shows the classes involved in the behavior of the elevator - specifically how it handles a user "taking" the elevator to another floor and how a user "calls" the elevator from a floor different from the floor the elevator currently resides. The solution shown is available here.
The first line of thinking about this problem was similar to that of the stopwatch. The elevator can be in one of three states: top, middle, or bottom corresponding to the third floor, second floor and first floor respectively. But our state diagram takes on extra states due to more than one behavior possible for a particular state. For example, the elevator in the top state can go either to the middle state (second floor) or to the bottom state (first floor). Thus, just like the stopwatch, we model the states of the elevator depending on what the user does.
The second degree of freedom in this application is the behavior of a "called" elevator (the user pushes the ""CALL" button) verses the behavior of the elevator when the user "takes" the elevator (uses one of the "GO" buttons). When a user "takes" the elevator to a floor, the textfield announcement is that the elevator arrives, the door opens and the user exits. When a user "calls" the elevator (uses one of the "CALL" buttons), the textfield announcement is that the elevator arrives and the door opens. Thus, a response to a "CALL" button cannot be implemented as though it were a response to a "GO" button. Another part of the problem is that there are two "GO" buttons on each floor. This makes it relatively easy to assign to a listener what state should handle a click on a "GO" button.
This UML class diagram does not show the classes involved in the GUI aspects of the application. This UML class diagram shows the classes involved in the behavior of the elevator - specifically how it handles a user "taking" the elevator to another floor and how a user "calls" the elevator from a floor different from the floor the elevator currently resides. The solution shown is available here.
Sunday, March 8, 2009
Challenge Four: Doktat's Elevator
Challenge Four involves an application that crudely simulates a three-floor elevator as shown in the schematic below. The code for the ElevatorGUIshell can be downloaded here. The challenge is to program this application without the use of any conditional logic.
The application starts with the elevator at the first floor with 'GO' buttons enabled and 'CALL' buttons on floors two and three also enabled. At this point the elevator can go to the second or third floor in response to either the 'GO' buttons on the first floor or the 'CALL' buttons on either the second or third floors.
Assume the user has clicked on the 'GO TO 2nd FLOOR' button. The configuration of the elevator application should appear as shown in the figure below.
Having gone to the second floor, the 'GO' buttons on the second floor are now enabled and the 'CALL' button for the second floor disabled. The 'GO' buttons on the first floor are now disabled but the 'CALL' button for the first floor is now enabled. The configuration of the third floor remains unchanged. The textfield for the first floor indicates the door has now closed and the textfield for the second floor indicates the elevator has arrived, the door has opened and the user has exited.
Assume now that a user has clicked on the 'CALL' button on the third floor. The configuration of the elevator should appear as shown in the schematic below.
Having arrived at the third floor the textfield indicates the elevator has arrived and the door has opened. Note that this message is different from the message shown when the elevator arrived at the second floor as the result of the user using the 'GO' button. Thus, the message shown is different in the textfield of the respective floor depending on whether the elevator was called to that floor or a user took the elevator to that floor. Again, the 'GO' buttons are now activated for the third floor but the 'CALL' button on the third floor is disabled. The textfield on the second floor indicates the door has closed and the 'CALL' buttons for both the second and first floors are enabled.
A solution employing no conditional logic will be available here.
The application starts with the elevator at the first floor with 'GO' buttons enabled and 'CALL' buttons on floors two and three also enabled. At this point the elevator can go to the second or third floor in response to either the 'GO' buttons on the first floor or the 'CALL' buttons on either the second or third floors.
Assume the user has clicked on the 'GO TO 2nd FLOOR' button. The configuration of the elevator application should appear as shown in the figure below.
Having gone to the second floor, the 'GO' buttons on the second floor are now enabled and the 'CALL' button for the second floor disabled. The 'GO' buttons on the first floor are now disabled but the 'CALL' button for the first floor is now enabled. The configuration of the third floor remains unchanged. The textfield for the first floor indicates the door has now closed and the textfield for the second floor indicates the elevator has arrived, the door has opened and the user has exited.
Assume now that a user has clicked on the 'CALL' button on the third floor. The configuration of the elevator should appear as shown in the schematic below.
Having arrived at the third floor the textfield indicates the elevator has arrived and the door has opened. Note that this message is different from the message shown when the elevator arrived at the second floor as the result of the user using the 'GO' button. Thus, the message shown is different in the textfield of the respective floor depending on whether the elevator was called to that floor or a user took the elevator to that floor. Again, the 'GO' buttons are now activated for the third floor but the 'CALL' button on the third floor is disabled. The textfield on the second floor indicates the door has closed and the 'CALL' buttons for both the second and first floors are enabled.
A solution employing no conditional logic will be available here.
Friday, March 6, 2009
Challenge Three: Depencency Inversion Principle and Retrofactoring
CHALLENGE THREE: DEPENDENCY INVERSION PRINCIPLE AND RETROFACTORING
CODE FOR THIS CHALLENGE (ZIP'D ECLIPSE PROJECT) AVAILABLE HERE.
The following are stated in Martin’s (2002, p. 127) discussion of the Dependency Inversion Principle (DIP).
a. "High-level modules should not depend on low-level modules. Both should depend on abstractions."
b. "Abstractions should not depend on details. Details should depend on abstractions."
The following schematics are recreations of figures from Martin (2002)
The problem with this configuration is that changing things in a lower layer, such as the Utility Layer can break things in the Mechanism Layer. Then Martin presents a preferred configuration.
With the use of this configuration, Martin (2002, p. 129) states:
"Using this inversion of ownership, PolicyLayer is unaffected by any changes to MechanismLayer or UtilityLayer. Moreover, PolicyLayer can be reused in any context that defines lower-level modules that conform to the PolicyServiceInterface. Thus, by inverting the dependencies, we have created a structure, which is simultaneously more flexible, durable and mobile."
A more specific example is then presented when the discussion includes a switch controlling a lamp as shown in the shematic below.
Martin offers the following preferred configuration with inverted dependency.
When preparing materials for my students, I often take refactored code and retrofactor it back to a point where they can then refactor it. In this challenge, I experienced an “ahah moment” when I was retrofactoring it and thought it might be useful to have the students try the same thing. The code I have provided has already been refactored to implement the dependency inversion principle (DIP). The schematic below shows the refactored configuration.
The student should remove the interface and configure the lamp (LightBulb or Fan) so that it is tightly coupled with the switch (SwitchPanel). To get the application to work, students should be aware of what must be done in terms of visibility/accessibility to establish the dependency?
CODE FOR THIS CHALLENGE (ZIP'D ECLIPSE PROJECT) AVAILABLE HERE.
The following are stated in Martin’s (2002, p. 127) discussion of the Dependency Inversion Principle (DIP).
a. "High-level modules should not depend on low-level modules. Both should depend on abstractions."
b. "Abstractions should not depend on details. Details should depend on abstractions."
The following schematics are recreations of figures from Martin (2002)
The problem with this configuration is that changing things in a lower layer, such as the Utility Layer can break things in the Mechanism Layer. Then Martin presents a preferred configuration.
With the use of this configuration, Martin (2002, p. 129) states:
"Using this inversion of ownership, PolicyLayer is unaffected by any changes to MechanismLayer or UtilityLayer. Moreover, PolicyLayer can be reused in any context that defines lower-level modules that conform to the PolicyServiceInterface. Thus, by inverting the dependencies, we have created a structure, which is simultaneously more flexible, durable and mobile."
A more specific example is then presented when the discussion includes a switch controlling a lamp as shown in the shematic below.
Martin offers the following preferred configuration with inverted dependency.
When preparing materials for my students, I often take refactored code and retrofactor it back to a point where they can then refactor it. In this challenge, I experienced an “ahah moment” when I was retrofactoring it and thought it might be useful to have the students try the same thing. The code I have provided has already been refactored to implement the dependency inversion principle (DIP). The schematic below shows the refactored configuration.
The student should remove the interface and configure the lamp (LightBulb or Fan) so that it is tightly coupled with the switch (SwitchPanel). To get the application to work, students should be aware of what must be done in terms of visibility/accessibility to establish the dependency?
Thursday, March 5, 2009
Challenge Two: Answer & Discussion
Many students quickly notice that the UML class diagram for the State Design Pattern is essentially the same as the Strategy Design Pattern. Both patterns use classes that provide concrete implementations of the desired behavior abstracted into an interface (or abstract class) . The primary difference between these two design patterns is the location of the responsibility for knowing what the next state should be when a certain action occurs. In a state diagram certain states can only transition to certain other states and when using the State Design Pattern , this knowledge is given to the concrete implementations of the classes representing these states.
Consider the following state diagram which displays the altered states incorporated into my refactored solution to the stopwatch challenge.
I have split the Running state into two states, RunningToHold and RunningToStop. Which state the running stopwatch is in depends on which button the user clicks to transition the stopwatch to its next state. If the user clicks on the ‘HOLD’ button, the stopwatch was in the RunningToHold state. If the user clicks on the ‘START/STOP’ button, the stopwatch was in the RunningToStop state. Likewise, for the same reason, the Hold state is partitioned into HoldToRunning and HoldToStopped states. These states have the responsibility for configuring what the current state is with respect to whatever button could be clicked in addition to controlling the clock. For example, consider the ZERO state.
The ZeroState class, which represents the state with name ZERO in the state diagram shown above, implements the execute method with the following code.
public void execute()
{
clock.start();
startStopButton.setCurrentState(runningToStoppedState);
startStopButton.setBackground(Color.green);
holdButton.setCurrentState(runningToHoldState);
holdButton.setBackground(Color.green);
// resetButton already set to nullState
}
After starting the clock, it prepares the Start/Stop button for what it should do next if the user were to click on Start/Stop. Likewise, it prepares the Hold Button. Since the Reset button will be unavailable and was already configured that way, no changes are made to the Reset button. For further discussion, assume the user will click on the Start/Stop button again after starting the watch. The RunningToStoppedState will be the state responsible for what is to happen next as shown in the following execute method.
public void execute()
{
clock.stop();
startStopButton.setCurrentState(nullState);
startStopButton.setBackground(Color.lightGray);
holdButton.setCurrentState(nullState);
holdButton.setBackground(Color.lightGray);
resetButton.setCurrentState(stoppedState);
resetButton.setBackground(Color.green);
}
After stopping the clock, the next state is the STOPPED state and the only button activated is the Reset button. Thus the Start/Stop and Hold Buttons are set to NullState and deactivated (colored gray) while the Reset Button is set to StoppedState and activated (colored green). Thus each transition to a new state in the stopwatch machine is accompanied by appropriate configuration of the buttons by activating/inactivating them and informing them what the next state of the machine is. There is no central control of the states of this machine via a monolithic conditional logic structure (the hallmark of procedural programming). If a new state was required, appropriate implementation of the execute method in the new state class plus adjustments in classes transitioning into the new class and out of the new class would be the only changes necessary.
In Challenge One, a Strategy Design Pattern was used to handle the two modes of the Bergin Swing Calculator. The refactored stopwatch also uses a Strategy Design Pattern to handle the two modes of the timer thread, suspended or running. This obviated the need for conditional logic to determine current state of the thread because with the use of the strategy, the thread already knew what state it was in and what to do.
The only conditional logic statements in the application are found in the Clock class where numbers are parsed and formatted. This is an example where conditional logic is not being used to determine the state of a class and hence control its behavior, and thus is out of the scope of the intent of the “Programming Without Ifs” exercises.
Consider the following state diagram which displays the altered states incorporated into my refactored solution to the stopwatch challenge.
I have split the Running state into two states, RunningToHold and RunningToStop. Which state the running stopwatch is in depends on which button the user clicks to transition the stopwatch to its next state. If the user clicks on the ‘HOLD’ button, the stopwatch was in the RunningToHold state. If the user clicks on the ‘START/STOP’ button, the stopwatch was in the RunningToStop state. Likewise, for the same reason, the Hold state is partitioned into HoldToRunning and HoldToStopped states. These states have the responsibility for configuring what the current state is with respect to whatever button could be clicked in addition to controlling the clock. For example, consider the ZERO state.
The ZeroState class, which represents the state with name ZERO in the state diagram shown above, implements the execute method with the following code.
public void execute()
{
clock.start();
startStopButton.setCurrentState(runningToStoppedState);
startStopButton.setBackground(Color.green);
holdButton.setCurrentState(runningToHoldState);
holdButton.setBackground(Color.green);
// resetButton already set to nullState
}
After starting the clock, it prepares the Start/Stop button for what it should do next if the user were to click on Start/Stop. Likewise, it prepares the Hold Button. Since the Reset button will be unavailable and was already configured that way, no changes are made to the Reset button. For further discussion, assume the user will click on the Start/Stop button again after starting the watch. The RunningToStoppedState will be the state responsible for what is to happen next as shown in the following execute method.
public void execute()
{
clock.stop();
startStopButton.setCurrentState(nullState);
startStopButton.setBackground(Color.lightGray);
holdButton.setCurrentState(nullState);
holdButton.setBackground(Color.lightGray);
resetButton.setCurrentState(stoppedState);
resetButton.setBackground(Color.green);
}
After stopping the clock, the next state is the STOPPED state and the only button activated is the Reset button. Thus the Start/Stop and Hold Buttons are set to NullState and deactivated (colored gray) while the Reset Button is set to StoppedState and activated (colored green). Thus each transition to a new state in the stopwatch machine is accompanied by appropriate configuration of the buttons by activating/inactivating them and informing them what the next state of the machine is. There is no central control of the states of this machine via a monolithic conditional logic structure (the hallmark of procedural programming). If a new state was required, appropriate implementation of the execute method in the new state class plus adjustments in classes transitioning into the new class and out of the new class would be the only changes necessary.
In Challenge One, a Strategy Design Pattern was used to handle the two modes of the Bergin Swing Calculator. The refactored stopwatch also uses a Strategy Design Pattern to handle the two modes of the timer thread, suspended or running. This obviated the need for conditional logic to determine current state of the thread because with the use of the strategy, the thread already knew what state it was in and what to do.
The only conditional logic statements in the application are found in the Clock class where numbers are parsed and formatted. This is an example where conditional logic is not being used to determine the state of a class and hence control its behavior, and thus is out of the scope of the intent of the “Programming Without Ifs” exercises.
Saturday, February 28, 2009
Challenge Two - State Design Pattern
In Programming Without Ifs Challenge One we identified the varying behavior which depended on the state of the calculator, and abstracted it with the introduction of a Strategy Design Pattern. Then when the calculator switched from the pre-operator mode to the post-operator mode, the calculator model also switched to the appropriate strategy. Thus, the calculator model was not kept in a state of ignorance. It didn't need to employ conditional logic to ask itself what state it was in to decide what to do - it already knew. The logic for switching between the strategies, ie., knowing when a change of state occurred and what to do about it, stayed on the calculator model. Our refactoring just implemented it in a different way. In Programming Without Ifs Challenge Two, we use the State Design Pattern, which moves the knowledge for what to do to the classes that characterize the state machine. Challenge Two involves programming a stopwatch.
Consider the scheme of the stopwatch interface shown below.
The stopwatch is started by clicking on the Start/Stop button. The watch is started as evidenced by the time label and the 'HOLD' button is activated. The watch can now be stopped or suspended as shown in the figure below.
The suspended mode is for timing splits. By clicking on the 'HOLD' button again, the watch will catch up to it's original timing and subsequent suspensions can again occur indefinitely. The watch can be stopped by clicking on 'START/STOP' from either the suspended mode or the running mode. After stopping the watch, the 'RESET' button will be activated as shown below.
After clicking on 'RESET' the watch will return to its starting mode as shown in the first schematic. A state diagram for the stopwatch is shown below.
Download for the code for the stopwatch with conditional logic and refactor such that no conditional logic is used to manage the state dependencies of the application. It may be necessary to create more states than shown in the state diagram above.
Consider the scheme of the stopwatch interface shown below.
The stopwatch is started by clicking on the Start/Stop button. The watch is started as evidenced by the time label and the 'HOLD' button is activated. The watch can now be stopped or suspended as shown in the figure below.
The suspended mode is for timing splits. By clicking on the 'HOLD' button again, the watch will catch up to it's original timing and subsequent suspensions can again occur indefinitely. The watch can be stopped by clicking on 'START/STOP' from either the suspended mode or the running mode. After stopping the watch, the 'RESET' button will be activated as shown below.
After clicking on 'RESET' the watch will return to its starting mode as shown in the first schematic. A state diagram for the stopwatch is shown below.
Download for the code for the stopwatch with conditional logic and refactor such that no conditional logic is used to manage the state dependencies of the application. It may be necessary to create more states than shown in the state diagram above.
Tuesday, February 24, 2009
Challenge One Answer & Discussion
To follow the discussion, download the refactored code from here.
In the refactored SwingCalculatorWithoutIfs, the conditional logic originally encountered in the JFrame is now replaced by giving to each button the responsibility for listening to itself. In general this is a good practice for GUI design. The frame or panel holding the components shouldn’t bear the responsibility for determining what is happening such as which button has been selected and hence, what method to invoke. These types of responsibilities are the domain of the objects themselves. Thus the buttons now have the responsibility for knowing when they’ve been selected and also what to do after they’ve been selected.
The second location where conditional logic was employed was in the CalculatorModel class itself. When numbers are entered into the calculator, it must be known whether the computation is in the pre-operator or post-operator mode. In the CalculatorModel class, the numberHit method is invoked whenever a number button has been hit by the user. Conditional logic is used to determine which mode the calculator is in, the pre-operator or the post-operator mode. A purist may argue that the use of any conditional logic statement violates a rule of object-oriented programming - that a method should only do one thing. Any conditional logic structure does at least two things: (1) determine the state of the Boolean conditional; and (2) do what needs to be done pending the result. Another way to think about this is to consider that whenever an object needs to ask itself, “What state am I in?” one must wonder why the object didn’t determine what it was to do when the state variable was changed. Why is it necessary to go back in time? Why is the object ignorant of both the state it is currently in and even worse, what to do about it? In the CalculatorModel class, the solution was to use a Strategy Design Pattern.
In the words of Shalloway (2005) we “find what varies and encapsulate it.” Although this sentiment appeared in the classic “Gang-of-Four” book (Gamma, 1990), it was Shalloway who stated the commonly used adage noted above. What’s varying here? The behavior of the numberHit method, depending on the mode the calculator is in. So, we create an interface named CalculatorStrategy that abstracts the numberHit method and we create two classes, PreOperatorStrategy and PostOperatorStrategy that provide concrete implementations for the method’s behavior in pre-operator and post-operator modes, respectively. In addition to instantiating these two strategies as CalculatorStrategy types, I also create a reference to a CalculatorStrategy type named currentStrategy. This will be set to whatever strategy is appropriate as determined by the changes in state of the CalculatorModel class. To complete the control of the strategy implementation, we include statements in the rememberOperator method, which is invoked when an operator button is hit, and the reset method, which is invoked when the equals button is hit, to assign the appropriate strategy to the currentStrategy. Now, we refactor the numberHit method in the CalculatorModel to invoke currentStrategy.numberHit and since the currentStrategy has already been adjusted to behave according to whatever state the calculator is in, there is no need to inquire as to its own state. The numberHit method is now loosely coupled to the implementation of the behaviors. The numberHit method doesn’t know how it is being implemented, it only knows that it is invoking a numberHit method on a CalculatorStrategy type class and therefore, the numberHit method must be implemented.
Lastly, one more ‘if’ to eliminate. When the equals button is hit, the operate method on the CalculatorModel is invoked which in turn invokes the reset method to prepare the calculator for a new computation. The reset method sets the value of the operator property to null. To avoid throwing a NullPointerException as a result of the user hitting the equals button before selecting an operator, we use conditional logic to check whether the value of operator property is null first before we try to invoke the operate method on it. The refactored SwingCalculator has eliminated this problem by creating an inner class named NullOperator that implements the OperatorButton interface but does nothing when the operate method is invoked. When the calculator is now reset, we set the operator property to NullOperator thus obviating the need to use conditional logic to check for a null value as described above.
REFERENCES:
Gamma, E., Helm,E., Johnson, R., and John Vlissides (1990) Design Patterns: Elements of Reusable Object-Oriented Software [ISBN 0201633612]
Shalloway, A., and Trott, J.R., (2005) Design Patterns Explained: A New Perspective on Object-Oriented Design [ISBN 0321247140]
In the refactored SwingCalculatorWithoutIfs, the conditional logic originally encountered in the JFrame is now replaced by giving to each button the responsibility for listening to itself. In general this is a good practice for GUI design. The frame or panel holding the components shouldn’t bear the responsibility for determining what is happening such as which button has been selected and hence, what method to invoke. These types of responsibilities are the domain of the objects themselves. Thus the buttons now have the responsibility for knowing when they’ve been selected and also what to do after they’ve been selected.
The second location where conditional logic was employed was in the CalculatorModel class itself. When numbers are entered into the calculator, it must be known whether the computation is in the pre-operator or post-operator mode. In the CalculatorModel class, the numberHit method is invoked whenever a number button has been hit by the user. Conditional logic is used to determine which mode the calculator is in, the pre-operator or the post-operator mode. A purist may argue that the use of any conditional logic statement violates a rule of object-oriented programming - that a method should only do one thing. Any conditional logic structure does at least two things: (1) determine the state of the Boolean conditional; and (2) do what needs to be done pending the result. Another way to think about this is to consider that whenever an object needs to ask itself, “What state am I in?” one must wonder why the object didn’t determine what it was to do when the state variable was changed. Why is it necessary to go back in time? Why is the object ignorant of both the state it is currently in and even worse, what to do about it? In the CalculatorModel class, the solution was to use a Strategy Design Pattern.
In the words of Shalloway (2005) we “find what varies and encapsulate it.” Although this sentiment appeared in the classic “Gang-of-Four” book (Gamma, 1990), it was Shalloway who stated the commonly used adage noted above. What’s varying here? The behavior of the numberHit method, depending on the mode the calculator is in. So, we create an interface named CalculatorStrategy that abstracts the numberHit method and we create two classes, PreOperatorStrategy and PostOperatorStrategy that provide concrete implementations for the method’s behavior in pre-operator and post-operator modes, respectively. In addition to instantiating these two strategies as CalculatorStrategy types, I also create a reference to a CalculatorStrategy type named currentStrategy. This will be set to whatever strategy is appropriate as determined by the changes in state of the CalculatorModel class. To complete the control of the strategy implementation, we include statements in the rememberOperator method, which is invoked when an operator button is hit, and the reset method, which is invoked when the equals button is hit, to assign the appropriate strategy to the currentStrategy. Now, we refactor the numberHit method in the CalculatorModel to invoke currentStrategy.numberHit and since the currentStrategy has already been adjusted to behave according to whatever state the calculator is in, there is no need to inquire as to its own state. The numberHit method is now loosely coupled to the implementation of the behaviors. The numberHit method doesn’t know how it is being implemented, it only knows that it is invoking a numberHit method on a CalculatorStrategy type class and therefore, the numberHit method must be implemented.
Lastly, one more ‘if’ to eliminate. When the equals button is hit, the operate method on the CalculatorModel is invoked which in turn invokes the reset method to prepare the calculator for a new computation. The reset method sets the value of the operator property to null. To avoid throwing a NullPointerException as a result of the user hitting the equals button before selecting an operator, we use conditional logic to check whether the value of operator property is null first before we try to invoke the operate method on it. The refactored SwingCalculator has eliminated this problem by creating an inner class named NullOperator that implements the OperatorButton interface but does nothing when the operate method is invoked. When the calculator is now reset, we set the operator property to NullOperator thus obviating the need to use conditional logic to check for a null value as described above.
REFERENCES:
Gamma, E., Helm,E., Johnson, R., and John Vlissides (1990) Design Patterns: Elements of Reusable Object-Oriented Software [ISBN 0201633612]
Shalloway, A., and Trott, J.R., (2005) Design Patterns Explained: A New Perspective on Object-Oriented Design [ISBN 0321247140]
Monday, February 23, 2009
Challenge One: Bergin Swing Calculator
During the time between 2005-2009 I began my software design courses with a problem I call the “Bergin Calculator Problem” (Inspired by Joe Bergin’s Polymorphic Calculator). It requires the student to program a simple calculator with only six buttons. Three number buttons (2,3,5) , two operator buttons (+,-) and an equals button. The calculator need only perform one binary operation before yielding an answer following a strike on the equals button. A typical computation would involve entering 2, 2, +, 3, 3, = … and the display would indicate the answer of 55. The display would show the numbers entered in both the pre-operator and post-operator modes, but not the operator itself. Thus, one would only ever see numbers in the display. Following each computation, striking the equals button would also render the calculator ready for a subsequent computation with an entirely new set of numbers.
Over ninety per cent of the submissions I graded used conditional logic to solve the problem. The week following, the class would go through the problem and together, construct a calculator that uses no conditional logic nor loop structures. The fact that this exercise amazes many students, indicates the very real possibility that most have been taught how to use an object-oriented language to do procedural programming - a very easy thing to do. In fact, I recall one graduate student who had taken the problem back to his work place and one of his co-workers, literally a rocket scientist, said the problem couldn’t be solved without the use of if’s. Since then, I have taken to giving problems to students that require them to produce a solution not dependent on conditional logic – I call this “Programming without if’s.”
I will discuss in another place the problem of over-engineering but for now assume it’s OK, in fact desirable, to construct an over-engineered solution. Why? The metaphor I like to use is that of the toolbox. When you go to your toolbox and see only a hammer, then every problem appears to be a nail. Most contemporary programmers were taught by procedural programmers whose only adaptation to learning object-oriented programming was to begin using an object-oriented language. One way to break out of this mental trap is to solve some problems without reaching for the hammer, which in the programmer’s case is the conditional logic structure.
Do I expect programmers to avoid using conditional logic as a best practice? No, the student of programming has to understand the difference between what we do to expand our capabilities, ie., put more tools in the toolbox, and what we do when programming professionally. Programming without if’s will expand your ability to see alternatives in the power offered to you by the object-oriented paradigm. If you’d like to take the Calculator Challenge a zipped Eclipse project named SwingCalculatorWithIfs is here. I will post a solution at the same address.
Over ninety per cent of the submissions I graded used conditional logic to solve the problem. The week following, the class would go through the problem and together, construct a calculator that uses no conditional logic nor loop structures. The fact that this exercise amazes many students, indicates the very real possibility that most have been taught how to use an object-oriented language to do procedural programming - a very easy thing to do. In fact, I recall one graduate student who had taken the problem back to his work place and one of his co-workers, literally a rocket scientist, said the problem couldn’t be solved without the use of if’s. Since then, I have taken to giving problems to students that require them to produce a solution not dependent on conditional logic – I call this “Programming without if’s.”
I will discuss in another place the problem of over-engineering but for now assume it’s OK, in fact desirable, to construct an over-engineered solution. Why? The metaphor I like to use is that of the toolbox. When you go to your toolbox and see only a hammer, then every problem appears to be a nail. Most contemporary programmers were taught by procedural programmers whose only adaptation to learning object-oriented programming was to begin using an object-oriented language. One way to break out of this mental trap is to solve some problems without reaching for the hammer, which in the programmer’s case is the conditional logic structure.
Do I expect programmers to avoid using conditional logic as a best practice? No, the student of programming has to understand the difference between what we do to expand our capabilities, ie., put more tools in the toolbox, and what we do when programming professionally. Programming without if’s will expand your ability to see alternatives in the power offered to you by the object-oriented paradigm. If you’d like to take the Calculator Challenge a zipped Eclipse project named SwingCalculatorWithIfs is here. I will post a solution at the same address.
Subscribe to:
Posts (Atom)