Showing posts with label strategy design pattern. Show all posts
Showing posts with label strategy design pattern. Show all posts

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.


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.

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.