For the past eight(8) years Schalk Neethling has been working as a freelance developer under the pseudo of Volume4 and is now the president of Overt Strategy Consulting. During this period he has completed over 300 projects ranging from full web application development to complete branding. As president and lead developer of Overt Strategy Consulting, Schalk Neethling and his team has released a 100% Java standards based content management system called AlliedBridge and business document exchange and review system, called Doc-Central. Schalk Neethling is also actively involved on a daily basis in the open source, web standards and accessibility areas and is a current active member of the Web Standards Group. Schalk is also the co-founder and president of the non-profit The South Web Standards and Accessibility Group, which aims to actively educate and raise awareness of web standards and accessibility to both the developer society as well as business large and small. Schalk also has a long relationship with DZone and is currently zone leader for both the web builder, css.dzone.com, as well as the .NET zone, dotnet.dzone.com, and you can find a lot of his writing there as well as on his blog located at schalkneethling.alliedbridge.com. Schalk is constantly expanding on his knowledge of various aspects of technology and loves to stay in touch with the latest happenings. For Schalk web development and the internet is not just a job, it is a love, a passion and a life style. Schalk has posted 173 posts at DZone. View Full User Profile

Design patterns and GWT

07.21.2008
| 68335 views |
  • submit to reddit
Communicating by observing events

We need to briefly elaborate on the event handling we have just introduced via CalculatorChangeListener. The general pattern, Observer, is how our calculator's model and view will be connected. It's also how default GWT components themselves generally communicate.

In addition to MVC, many developers will be accustomed to event handling. Some non-GUI programmers may also have realized the power of this approach and may be using it on the server side. If you're already familiar with events, you may find our initial examples here primitive. This is because we're trying to simplify and demonstrate the concepts, rather than to create the most efficient or streamlined code. (For example, we're not using such standard Java event idioms as PropertyChangeSupport and related constructs.).

This approach should bring those who have not yet encountered event-based and asynchronous programming, especially in relation to a GUI, up to speed quickly. We'll bring the pattern, itself, to the forefront with our first manually driven example. Listening for, and notifying of, events are all aspects of the Observer pattern. Figure 3 illustrates this pattern.

Figure 3 The Observer pattern, which is used in GWT to connect the model and view

In our example, the CalculatorChangeListener is used to register our view component (our CalculatorWidget itself) with the data portion of our model (CalculatorData). This is basically the first half of an observer/observable relationship between these components. Our change listener interface is shown in listing 4.

Listing 4 CalculatorChangeListener.java

public interface CalculatorChangeListener {
public void onChange(CalculatorData data);
}

CalculatorChangeListener has a single callback method, onChange() . This is a fairly typical basic listener interface. Our CalculatorData model component, as we'll see in a bit, makes itself observable (available for listeners to listen to) by implementing another simple interface, CalculatorChangeNotifier. This change notifier interface is shown in listing 5.

Listing 5 CalculatorChangeNotifier.java

public interface CalculatorChangeNotifier {
public void addChangeListener(
final CalculatorChangeListener listener);
}

CalculatorData, as shown in listing 6, therefore carries out the other half of the observer/observable relationship and notifies any listeners that come along when things change.

Listing 6 CalculatorData.java: the data portion of the model layer:

public class CalculatorData implements CalculatorChangeNotifier {
private String display;
private double buffer;
private boolean initDisplay;
private boolean lastOpEquals;
private CalculatorChangeListener listener;

public CalculatorData() {
this.clear();
}

public void addChangeListener(
final CalculatorChangeListener listener) {
this.listener = listener;
}

public void clear() {
this.display = "0";
this.buffer = 0.0;
this.initDisplay = true;
this.lastOpEquals = false;
if (listener != null) listener.onChange(this);
}

public double getBuffer() {
return buffer;
}

public void setBuffer(double buffer) {
this.buffer = buffer;
listener.onChange(this);
}

public String getDisplay() {
return display;
}

public void setDisplay(String display) {
this.display = display;
listener.onChange(this);
}

//...

CalculatorData allows a single listener to register , and then, when its own setters are invoked, it notifies that listener of the change. Again, keep in mind that we have simplified the approach here. In the real world, as is the case with GWT's own components that use this same pattern, you will likely see more than one listener attached to an observable in a collection, and hierarchies of specialized interfaces or abstract classes used for both observable and observer support (and you will need support to remove and clean up observers).

Along with the event mechanism, you probably have also noticed by now that the CalculatorWidget makes references to CalculatorConstants. This is a simple constants class that defines CSS styles and String constants such as: ADD, SUBTRACT, SQRT, and EQUALS. Now that we have our CalculatorWidget in place and our event handling taken care of, we'll go back to the remainder of our example. This means we need to address ButtonDigit and ButtonOperator, where we’ll meet the other half of our model, the logic.

Operator strategy

As we saw in the code in the 'Creating a widget' section, CalculatorWidget makes use of several custom button types. The buttons of a calculator handle the numeric and operator input. Buttons are, of course, view components, but the buttons we'll create to fill these roles are also backed by the logic portion of our model, our calculator's operators. We have used our own specific Java types, ButtonDigit and ButtonOperator, so that we can easily distinguish the logical types of buttons pressed, and so that we can further encapsulate the logic for each operation. Listing 7 shows our ButtonDigit implementation.

Listing 7 ButtonDigit.java

public class ButtonDigit extends Button {
public ButtonDigit(
final CalculatorController controller,
final String label) {
super(label);
this.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
controller.processDigit(label);
}
});
this.setStyleName(CalculatorConstants.STYLE_BUTTON_DIGIT);
}
}

ButtonDigit is a very straightforward extension of the GWT Button class. It simply includes a CalculatorController object reference in its constructor , and then invokes the controller's processDigit() method each time a ButtonDigit instance is clicked . This is a repeat of the pattern we're using in the outer CalculatorWidget, using the same controller reference. This invocation is achieved via a ClickListener.

The primary point in this example, especially for server-oriented developers, is that extending the basic UI classes is not only possible, but desirable. Using OOP techniques to extend functionality allows for much cleaner separation and reuse of code. This is in contrast to HTML-based development, where the <input type="button"> represents an opaque instruction. This fairly simple example just passes on a value, but you should note that the MVC pattern is present on multiple levels.

In listing 8 we see this pattern again with the ButtonOperator class. In this case, MVC comes into play on a micro level, while it's also used on a macro level with the entire outer Widget. ButtonOperator has a model, represented by the label; a view, managed by the parent class of Button, tweaked by the designation of a style to the button; and a controller layer represented by an AbstractOperator instance, which translates actions up to the larger scope controller for the calculator widget.

Listing 8 ButtonOperator.java

public class ButtonOperator extends Button {
public ButtonOperator(final CalculatorController controller,
final AbstractOperator op) {
super(op.label);
this.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
controller.processOperator(op);
}
});
this.setStyleName(CalculatorConstants.STYLE_BUTTON_OPERATOR);
}
}

ButtonOperator works in much the same way as ButtonDigit does. It extends the GWT Button class and includes a CalculatorController object reference in its constructor . Then it invokes the controller's processOperator() method each time a ButtonOperator instance is clicked . The difference with ButtonOperator is that it includes a reference to an AbstractOperator class in its constructor.

Now that we have operator buttons, we obviously need operations to back these view components. To complete these components, we'll use the Strategy pattern to encapsulate the logic. Rather than a monolithic if/else logic block, we'll delegate each operation to a specified instance of an operator class (each an implementation of AbstractOperator) and let those classes update our calculator's model. Using our operators in this manner makes our calculator easier to understand and allows for greater flexibility and extensibility. With this approach, we can add new operators later without affecting our existing logic. Figure 4 shows the Strategy pattern.

Figure 4 The Strategy pattern used in the GWT calculator example to implement operators

The only exceptions to this strategic structure for operators are the CLEAR and EQUALS operations, which are handled directly by the calculator controller, not by operator subclasses. Doing it this way requires less code and makes things a bit clearer. (We have some global state information in the controller that CLEAR and EQUALS rely upon. A purist implementation could put this information in our model and make separate operators for CLEAR and EQUALS as well, but that would complicate the other operations just for the sake of the pattern, and we think that would be overkill in this instance.)

These concepts may seem slightly off the beaten GWT path, but they serve to demonstrate the important point that you can use many OO design principles within the context of the toolkit. This flexibility is one of the advantages of GWT. In addition, this approach provides a much more robust calculator at the end of the day.

To handle the individual operators for our calculator, we need to implement the AbstractOperator and the subclasses that will extend it. As we saw in listing 8, ButtonOperator includes a reference to AbstractOperator. This is because each operator needs to do something different within a calculator application; each has a unique behavior. AbstractOperator, the straightforward beginning of the hierarchy, is shown in listing 9.

Listing 9 AbstractOperator.java

public abstract class AbstractOperator {
public String label;

AbstractOperator(final String label) {
this.label = label;
}
public abstract void operate(
final CalculatorData data);
}

AbstractOperator defines a single abstract method, operate(), which takes CalculatorData as input and updates it accordingly . We further divide operators with abstract types that determine whether or not the operator being implemented is binary or unary. BinaryOperator and UnaryOperator are as follows:

public abstract class BinaryOperator extends AbstractOperator {
BinaryOperator(final String label) {
super(label);
}

public abstract class UnaryOperator extends AbstractOperator {
UnaryOperator(final String label) {
super(label);
}
}

With our operator abstractions in place, we'll now implement a simple concrete operator for addition. (We'll not explicitly cover all the concrete operators used in our CalculatorWidget in the text for the sake of brevity). Listing 10 displays OperatorAdd.

Listing 10 OperatorAdd

public class OperatorAdd extends BinaryOperator {

public OperatorAdd() {
super(CalculatorConstants.ADD);
}

public void operate(final CalculatorData data) {
data.setDisplay(String.valueOf(data.getBuffer() +
Double.parseDouble(data.getDisplay())));
data.setInitDisplay(true);
}
}

The binary addition operator adds the current buffer to the current display value, and then updates the display . Binary operators in this context are basically responsible for updating the display, based on the values in the CalculatorData object. Also, this operator sets the initDisplay status to true ; this indicates that, when the next digit is entered, the display should start over rather than append digits to any possible existing value. Now, with all of the other aspects of our CalculatorWidget in place, we can move on to the controller.

Controlling the action

The calculator's controller handles the interaction between the other calculator components. The controller is called upon by the various buttons to perform actions such as invoking an operator or otherwise manipulating the model or internal state.

The GWT CalculatorWidget controller component is significant for several reasons beyond just the separation of responsibilities derived from the MVC pattern itself. We'll write this component in Java, and it will be compiled into JavaScript by GWT along with our UI components, yet this has nothing to do with the interface. GWT allows you to create not only your UI but also any logic and data representations with client-side intentions. You can create classes, like we're about to do with our CalculatorController and have already done with our CalculatorData, which end up in the client browser as completely non-view-related items.

In the CalculatorController, which is presented in listing 11, notice that there are no references to GWT-related classes.

Listing 11 CalculatorController.java

public class CalculatorController {

CalculatorData data;
AbstractOperator lastOperator;
Private double prevBuffer;

public CalculatorController(
final CalculatorData data) {
this.data = data;
}

public void processClear() {
data.clear();
lastOperator = null;
}

public void processEquals() {
if (lastOperator != null) {
if (!data.isLastOpEquals()) {
prevBuffer = Double.parseDouble(data.getDisplay());
}
lastOperator.operate(data);
data.setBuffer(prevBuffer);
data.setLastOpEquals(true);
}
}

public void processOperator(
final AbstractOperator op) {
if (op instanceof BinaryOperator) {
if ((lastOperator == null) || (data.isLastOpEquals())) {
data.setBuffer(Double.parseDouble(data.getDisplay()));
data.setInitDisplay(true);
} else {
lastOperator.operate(data);
}
lastOperator = op;
} else if (op instanceof UnaryOperator) {
op.operate(data);
}
data.setLastOpEquals(false);
}

public void processDigit(final String s) {
if (data.isLastOpEquals()) {
lastOperator = null;
}
if (data.isInitDisplay()) {
if (data.isLastOpEquals()) {
data.setBuffer(0.0);
} else {
data.setBuffer(Double.parseDouble(data.getDisplay()));
}
data.setDisplay(s);
data.setInitDisplay(false);
} else {
if (data.getDisplay().indexOf(
CalculatorConstants.POINT) == -1) {
data.setDisplay(data.getDisplay() + s);
} else if (!s.equals(CalculatorConstants.POINT)) {
data.setDisplay(data.getDisplay() + s);
}
}
data.setLastOpEquals(false);
}
}

CalculatorController is instantiated with a new instance of CalculatorData . Along with the data object member, our controller also has four main actions: equals() and clear(), which are used for the calculator operations of the same names, and processOperator() and processDigit() , which are used when their respective operator buttons are clicked.

Controller actions, such as the pressing of a digit or an operator, update the model, which fires the corresponding events. When the data in the model changes, in response to actions from the controller, components that are registered to listen will be notified. Our view, CalculatorWidget, is one such component. The controller also contains a reference to the lastOperator so that it can make decisions about what to do in the chain of operations it provides. Figure 5 shows an overview of the classes involved in our now-complete MVC-enabled calculator project.

Figure 5 Overview of the classes involved with the GWT calculator example

This use of CalculatorController allows our client-side application, all by its lonesome, to handle state, logic, delegation, and the use of a separate model, along with the view. It's important to remember this expression of the MVC pattern, and where the responsibilities lie. Java developers accustomed to the request-response cycle will be used to the view being rendered in a single, generally procedural action based on a single-state model. In GWT, as in well-designed desktop applications, the model can change independently of a singular view layer. This is notably different from a more transient view that performs a single operation and then goes away. Dealing with the state of the view, not just of the model, is something you will need to keep in mind during your development.

Now we have an event-driven GWT calculator in the form of a reusable CalculatorWidget. In addition, our code is fairly resilient to change and is extensible because we have used an OO approach with operators responsible for their own individual operations.

This article is excerpted from Chapter 1 of GWT in Practice, by Robert Cooper and Charlie Collins, and published in May 2008 by Manning Publications.

Published at DZone with permission of its author, Schalk Neethling.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Adrian Mouat replied on Thu, 2010/10/21 - 5:11am

Nice article, but I think there are some potential issues:

 - the listener needs to be checked for null everywhere in the observable

 - there seems to be potential synchronization issues

 On a side-note, the sign-up for this site is obnoxious, it asks for several personal details and has an unusual idea of what an address is (you *have* to enter a number!).

 

Adrian Mouat replied on Tue, 2010/10/26 - 6:36am

See also this (google) article:

 http://code.google.com/webtoolkit/articles/mvp-architecture.html

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.