HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Building large systems requires the difficult and time-consuming activities of elicitation and representation of software requirements. During these analysis activities, particular analysis abstractions emerge. These abstractions, called analysis patterns, represent reusable patterns for subsequent analysis efforts in various domains. As an example, software developers use an analysis abstraction called Person to represent a person from different application domains, such as a student person, employer person or customer person. Martin Fowler, in his book Analysis Patterns, has defined a higher abstraction to represent either a person or an organization labeled the Party pattern.

The most popular patterns - design patterns - deal with those patterns useful in both object-oriented design and programming. Design patterns represent reusable software structures needed for implementation. By contrast, Fowler's Analysis Patterns represents reusable abstractions needed in the elicitation and representation of the software requirements, hence analysis patterns. Analysis patterns represent conceptual domain structures denoting the model of a business system domain, rather than the design of computer programs. While analysis patterns don't deal directly with the details of implementation, they do influence how code is designed.

Why Use Analysis Patterns?
We've used patterns in structured programming and they proved to be beneficial. When writing a new program, most programmers copied examples of similar programs (program patterns) and modified them to meet the needs of the new program, rather than writing from scratch and risking simple compilation and logic errors. Structured pattern programs proved beneficial because they saved development and maintenance time needed for new programs and for reviewing existing programs.

When object-oriented analysis, design and programming were introduced, they proclaimed the notable advantage of reuse. These proclamations gave hope to the idea of fast, easy software development using reusable objects or components. Many object-oriented programmers aren't realizing reductions in their work efforts due to reuse. However, Fowler's analysis patterns will allow the proclamations to be realized in object-oriented language solutions.

The great advantage of using patterns in object-oriented development is the increase in productivity and therefore a reduction in cost. Analysis patterns leverage the capabilities of the reusability of object-oriented components. These components ease the complexity of problem solutions of new development and lower the maintenance for an application using these components. Application builders who have built systems using analysis patterns have experienced reduced development time, ease of development and low maintenance costs.

Fowler's First Pattern - The Party Pattern
The first analysis pattern described by Fowler is the Party pattern. A Party, according to Fowler, is an abstraction to define persons or organizations. He models a Party class with subclasses of Person and Organization. The Party could have different roles. For example, a person could be an employer, employee, doctor or mother. An organization could be a business entity, a shelter or a hospital. Figure 1 depicts the class representation of the Party pattern.

Figure 1

Observation Pattern
Another analysis pattern described by Fowler is the Observation pattern used to model information about the real world, generally representing attributes observed about a party. Observations play an important role in information systems, since these observations are usually stored in databases and rehashed to form statistical analysis of data. Examples of observations are a person's eye color, hair color, weight or height. Other examples include a test score of Johnny Doe on December 4, 1999, or Jane Doe's telephone number. The Observation pattern is an abstraction that describes the quantification about a given attribute type, called a phenomenon, related to a Party. As an example, a test score type of attribute - or, as labeled by Fowler, a test score phenomenon - has a quantification of 64, which is related to a person, Johnny Doe. For example, Johnny Doe made a score of 64 on a particular test.

Using the Observation pattern, new observations are defined by extending the behavior of the observation and writing new code to define their type. From a position paper by Jospeh Yoder, "Patterns for Developing Successful Object-Oriented Frameworks," we realize that there's not only an observation in the pattern, but an observation type is needed to describe the subject of the associated observation. Figure 2 depicts the relationship between a party as well as a portion of the Observation pattern, including the observation and observation type.

Figure 2
Figure 2:

The observation type allows modeling of the observation phenomenon as a type. This pattern allows creation of a phenomenon, such as test scores, without creating a different class for each phenomenon needed within a system. One instance of the party class, such as Johnny Doe, may have multiple instances of observations, and each of those observations is defined as a specific observation type. For example, John Doe has an observation of blue observation type called eye color, as well as an observation of 64 observation-type test scores.

One of the key characteristics of observations with information systems is the entry of observation information by users and the potential database storage of that information. This input scenario requires that information gathered from the user must be validated prior to placing the values into database storage. Integers, entered as strings in text fields, require validations by ensuring that first, they're composed of the characters 0-9 and second, that the value is indeed storable as an integer and the value of the entered data falls within the range defined by a business rule. A salary within an organization, represented as a float, may have a business rule that requires the salary to be more than $1 and less than $100,000 per year. In most structured systems, the validation routines necessary would be written into each program that allows entry of the field. In object-oriented systems, one might call a class that contains the method to validate salary. Using Fowler's analysis patterns, validation for this salary observation is done using the Validator pattern.

Validator Pattern
The Validator pattern is used in collaboration with the Observation pattern to validate observations. This architecture allows different types of observations to be associated with their relevant applications and businesses. Therefore, the observation types are extended with a validator type that associates the instance of the observation type with a validator strategy.

The Validator pattern is an abstraction that models the procedures for validation of different types of observations using three different validators. The Validator class (see Figure 3) depicts the three validators: discrete, range and null.

Figure 3
Figure 3:

When using the pattern, all observations are validated. Therefore, a need exists for a null validator in those fields that require no validation. The null validator is implemented using the Null Object pattern.

The Discrete validator authenticates values such as the items found in a typical code value table. A common example of discrete validation is eye color, in which valid values include a set with blue, green, hazel, brown and black members. Another common example is a two-member set representing sex as the discrete values of male or female. A user entering data in a field with a discrete validator would view a table of valid values and select one. This is possible if the number of values in the code table isn't too large. Large code tables often require the user to key in a few characters before a table of valid values is displayed. Observations in which the valid values are extremely large may not allow the code values to be displayed, but instead require the user to key in the entire value before validation. This is the scenario addressed by the discrete validation described in this paper.

The Range validator is used to validate an observation expected to be within a valid range of values according to a particular business rule. The salary, which must fall within the range of $1 to $100,000 per year, is an example of such an observation. A range validation routine requires a minimum and maximum value to define the valid range. The Validator Pattern classes are shown in Figure 3, each with a method isValid.

Not only does the validator abstraction allow users the freedom to reuse the validator component in many different domains, it also allows users to modify their validation requirements dynamically at runtime.

When code tables were introduced in the 1980s, the user gained the ability to add, modify and delete values from the code table without causing a program maintenance activity. Using the Range validator allows the user the same freedom - to change the minimum (min) and maximum (max) values for range-validated fields without asking a programmer to perform a maintenance activity. The Validator pattern uses the data dictionary table to obtain the min and max values for a range-validated field. The users have the freedom to change these data dictionary min and max values. Keeping this close collaboration between the data dictionary and the observation entry allows user control of validation rules and gives dynamic validation of observations at runtime. It demands a data dictionary that acts more as a usable software engineering tool rather than a documentation table.

Each observation, defined in the data dictionary, is linked to an observation type defining the validator needed for that observation. The observation "shoe size" is defined in the data dictionary with a minimum of four and maximum of 20. The shoe size observation type is called Shoesize and the validator defined by that type is RangeValidator.

Building Reusable GUI Components Using the Validator Pattern
Ease of development and maintenance is the goal in using analysis patterns. The Validator pattern allows easy building and maintenance of observations in an application domain. It also allows development of reusable graphical user interface (GUI) components. To show the technique of using the Validator pattern to build these reusable GUI components, this article concentrates on the Range Validator.

The data dictionary used to store the min and max necessary for range validation could also contain information regarding the needed GUI component for the observation. Items that prove useful in dynamically building GUI components for observations include the field length to dictate the size of the needed textfield, observation type (such as eye color) to attach a validator to the observation type, the default label needed on the GUI component, a standard default message for invalid data and the name of the validator (in this case range validator)

To build a reusable GUI panel component responsible for validating the users observation inputs, we define a panel named Observation Panel. The panel contains two components: the textfield of the length defined in the data dictionary and the label of the field with the name taken from the data dictionary, if desired. The panel contains a field that defines the data dictionary item contained in a vector named a data dictionary (DD) vector. The structure needed for this reusable component is shown in Figure 4.

Figure 4
Figure 4:

To understand fully how these GUI components would be used with the Observation and Validator patterns, we define a typical application programmer building a GUI under the present technology without analysis patterns, then with them.

A typical application program building a GUI screen follows a development scenario similar to the one below:

  1. Create the container for the screen
  2. Repeat for each component needed on the screen:
    a. Drag a reusable component (such as a label) on the screen
    b. Change the properties for the component (size, name, position on the screen, etc.)
    c. Write the validation routine for the component entry
    d. Write the error-routine display routine
    e. Test properties, location, validation routine, error routine iteratively
  3. Scenario complete
This scenario is time consuming and yields components that may have inconsistent labels, error messages and sizes across multiple screens. Even if programmers build components for each field entered, the validation and error routine must be written and tested for each component.

Using the analysis patterns described and a reusable bean - Observation Panel - component modeled in Figure 4, the scenario is as follows:

  1. Create the container for the screen
  2. Build the reusable Observation Panel Bean (once)
  3. Test the Observation Panel Bean
  4. Repeat for each needed component
    a. Drag the Observation Panel Bean on the screen
    b. Change the Data Dictionary Property Name for this field
    c. Test property DD name is correct
  5. Scenario complete

This scenario allows an expert bean builder to build and test the Observation Panel Bean (OPB) before the application programmer begins building the GUI screens using the OPB. The application developer reuses the Panel Bean component for each GUI component on each screen, which reduces the design, programming and testing effort significantly. Figure 5 shows the application developer changing the name of the data dictionary data element with IBM's VisualAge for Java.

Figure 5
Figure 5:

Runtime Scenarios
At runtime, the OPB displays itself and its components making itself capable of accepting and validating the entered data. When the OPB is displayed the following scenario happens.

  1. The new GUI screen frame creates an instance of the OPB.
  2. The OPB displays the label and textfield using information obtained from the data dictionary regarding size and label.
  3. The observation and observation-type instance are created. The specific validator is created using the Reflection pattern, and the needed data such as the min and max values are loaded.
This scenario is the result of executing the code shown in Listing 1, which includes the classes for the Observation, ObservationType, ObservationPanel and DataDictionaryRecord. Not all of the code is shown - only the parts of interest are displayed. The remainder of the code for the Validator and RangeValidator classes is shown in Listing 2.

After the OPB is displayed with the label and textfield, the data is entered by the user and validated with the Validator pattern as in the following scenario.

  1. An action is performed indicating that the field is entered and ready for validation. This may be a submit button on the GUI screen frame, a carriage return on the textfield or other actions.
  2. The OPB requests the text from the textfield using getText().
  3. The panel asks the observation to validate itself. This observation field will later hold the valid data in the needed type. The observation, expecting to receive a notification of valid data, asks the observation type to validate the string entered in the textfield using the validator defined according to the data dictionary information regarding the data type and valid ranges.
  4. The observation type sends the data dictionary member to the validator for the actual validation of the string data entered.
  5. If the data isn't valid, an error routine is executed. In our case an invalid message obtained from the data dictionary is displayed on the panel as a label. The message is erased if the user enters more data.
  6. If the data is valid, the panel requests the observation panel to convert the valid data to the value needed.

Remember, the only action performed by the application builder was to drag the OPB on the GUI screen frame and modify the bean property to be the specific data dictionary element. The remainder of the actions, from the two scenarios described above, happened as a result of methods and procedures contained in the reusable OPB, the data dictionary and the Validator analysis pattern. This same OPB and Validator can be reused for any data dictionary field on any GUI screen.

Conclusion
Analysis and design patterns are useful in solving problems when defining requirements for a system, irrespective of the domain. They're helpful in guiding the development of reusable components. Fowler's Observation patterns are examples of analysis patterns that have many variations and extensions. The OPB reusable component and the Validator pattern (see Figure 4) use the Observation pattern with an object-oriented language and its concepts to promote the reusability of the Validator and the OPB. The discrete validator would be implemented similarly with the name of the valid code table values contained in the data dictionary. Use of the Validator pattern certainly changes the face of GUI development.

While Fowler's patterns are powerful abstractions in information systems, implementing them in different languages often leads to many nuances and problems that require further understanding of the patterns and the problems solved by them.

In the future, validation functions could be linked to observations, composite validation rules added to composite observations, and other GUI components and debugging tools developed. These types of abstractions and the resulting codes do take time, but the payoff in productivity is certainly large enough to justify the cost.

Resources

  1. Gamma, E., Helms, R., Johnson, R., and Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA. Addison-Wesley.
  2. Coplien, J.O., and Schmidt, D.C. (1995). Pattern Languages of Program Design. Reading, MA. Addison-Wesley.
  3. Fowler, M. (1997). Analysis Patterns: Reusable Object Models. Reading, MA. Addison-Wesley.
  4. Grand, M. (1998). Patterns in Java. Vol. 1. New York, NY. John Wiley & Sons, Inc.
  5. Grand, M. (1999). Patterns in Java. Vol. 2. New York, NY. John Wiley & Sons, Inc.
  6. Yoder, J. "Patterns for Developing Successful Object-Oriented Frameworks." Workshop Position Paper, OOPSLA, 1997.
Author Bios
Dr. Sara Stoecklin serves as graduate director in the Department of Computer and Information Sciences at Florida A&M University where she teaches software engineering classes in industry and academia. Dr. Stoecklin holds an MS from East Tennessee State University and a PhD from Florida State University. [email protected]

Dr. Clement Allen teaches advanced Java at Florida A&M University. He holds an MS from Howard University and a PhD from the University of Alabama at Birmingham. [email protected]

	

Listing 1 

//Observation Class with a few pertinent methods 
public class Observation { 
   private java.util.Date recordedDate; 
   private java.util.Date observedDate; 
   private int duration; 
   private ObservationType type; 
   private String observationTypeName; 
   public boolean isValid (String obsValue) { 
    return getType().isValid( obsValue ); 
   } end isValid 
} // end Observation Class 

// Observation Type with pertinent methods 
public class ObservationType { 
   private String phenomenon; 
   private Validator dataElementValidator = null; 
   private String phenomenonType; 
   public ObservationType(String ddElementName) { 
     DataDictionaryRecord ddrecord = DDManager.getMember(dElementName ); 
     setPhenomenon( ddElementName ); 
     setPhenomenonType( ddrecord.getDataElementType()); 
     try { 
    // This uses the validator name stored in the data dictionary to    // 
build an instance of the needed validator (either a Range 
    // or Discrete Validator) using the Reflection pattern. 
    Class validatorClass = 
      Class.forName("fowlerspatterns."+ddrecord.getValidatorName() ); 
    setDataElementValidator ( (Validator) 
    validatorClass.newInstance()); 
    getDataElementValidator().setDDRecord( ddrecord); 
     } catch(Exception e) { e.printStackTrace(); } 
   } // end ObservationType constructor 
   public boolean isValid(String obsValue) { 
        return getDataElementValidator().isValid(obsValue ); 
   } // end isValid 
} // end Observation Type 

// Observation Panel 

public class ObservationPanel extends java.awt.Panel 
implements java.awt.event.ActionListener { 
    protected transient 
        java.beans.PropertyChangeSupport propertyChange; 
    private String fieldDataDictionaryElementName = new String(); 
    private java.awt.Label ivjObservationLabel = null; 
    private java.awt.TextField 
ivjObservationTextField = null; 
    private Observation dataElementObservation = null; 
    private java.awt.Label ivjErrorLabel = null; 
    private boolean observationValid = true; 
    private String panelObservation; 
    private String panelObservationText; 
    private String errorText; 

    public ObservationPanel() { 
    super();    initialize(); 
    } // end ObservationPanel constructor 

    public static void main(java.lang.String[] args) { 
    try {   java.awt.Frame frame; 
        try { 
           Class aFrameClass = 
            Class.forName("com.ibm.uvm.abt.edit.TestFrame"); 
          frame = (java.awt.Frame)aFrameClass.newInstance(); 
        } catch (java.lang.Throwable ivjExc) 
            {frame = new java.awt.Frame();} 
        ObservationPanel aObservationPanel; 
        aObservationPanel = new ObservationPanel(); 
        frame.add("Center", aObservationPanel); 
        frame.setSize(aObservationPanel.getSize()); 
        frame.setVisible(true); 
    } catch (Throwable exception) { 
        System.err.println("Exception occurred in main Panel"); 
        exception.printStackTrace(System.out); 
    } 
    }// end main 

    public void actionPerformed(java.awt.event.ActionEvent e) { 
    if ((e.getSource() == getObservationTextField()) ) { 
        validateThisObservation(e); 
    } 
    } // end actionPerformed 

    private void validateThisObservation 
        (java.awt.event.ActionEvent arg1) { 
    try {this.validateObservation();} 
    catch (java.lang.Throwable ivjExc) {handleException(ivjExc);} 
    } // end validateThisObservation 

    private java.awt.Label getErrorLabel() { 
    if (ivjErrorLabel == null) { 
        try { 
            ivjErrorLabel = new java.awt.Label(); 
            ivjErrorLabel.setName("ErrorLabel"); 
            ivjErrorLabel.setText(""); 
            ivjErrorLabel.setBounds(45, 61, 269, 23); 
        } catch (java.lang.Throwable ivjExc) 
            {handleException(ivjExc);} 
    }; // end if 
    return ivjErrorLabel; 
    } // end getErrorLabel 

    private java.awt.Label getObservationLabel() { 
    if (ivjObservationLabel == null) { 
        try { 
            ivjObservationLabel = new java.awt.Label(); 
            ivjObservationLabel.setName("ObservationLabel"); 
            ivjObservationLabel.setText("                     "); 
        ivjObservationLabel.setBackground(java.awt.Color.cyan); 
            ivjObservationLabel.setBounds(36, 29, 66, 23); 
        } catch (java.lang.Throwable ivjExc) 
                  {handleException(ivjExc);} 
    }; // end if 
    return ivjObservationLabel; 
    } // end getObservationLabel 

    private java.awt.TextField getObservationTextField() { 
    if (ivjObservationTextField == null) { 
        try { 
            ivjObservationTextField = new java.awt.TextField(); 
              ivjObservationTextField.setName 
                ("ObservationTextField"); 
            ivjObservationTextField.setBounds(108, 29, 188, 23); 
        } catch (java.lang.Throwable ivjExc) 
                {handleException(ivjExc);) 
    }; // end if 
    return ivjObservationTextField; 
    }// end getObservationTextField 

    protected java.beans.PropertyChangeSupport getPropertyChange() { 
    if (propertyChange == null) { 
        propertyChange = new 
            java.beans.PropertyChangeSupport(this); 
    }; // end if 
    return propertyChange; 
    } // end PropertyChangeSupport 

    private void initialize() { 
    setName("ObservationPanel"); setLayout(null); setSize(375, 88); 
    add(getObservationTextField(), 
getObservationTextField().getName()); 
    add(getObservationLabel(), getObservationLabel().getName()); 
    add(getErrorLabel(), getErrorLabel().getName()); 
    initConnections(); 
    DataDictionaryRecord ddrecord = 
        DDManager.getMember(getDataDictionaryElementName()); 
    getObservationLabel().setText( ddrecord.getLabelName() ); 
    setErrorText( ddrecord.getInvalidObservationLabel() ); 
    // The label size, textfield size, and panel size should 
    // be adjusted using the length of the label from the 
    // data dictionary. 
        setDataElementObservation( new Observation( 
getDataDictionaryElementName() ) ); 
    } // end initialize 

    public void setDataDictionaryElementName 
        (String dataDictionaryElementName) { 
    String oldValue = fieldDataDictionaryElementName; 
    fieldDataDictionaryElementName = dataDictionaryElementName; 
    firePropertyChange 
        ("dataDictionaryElementName", oldValue, 
        dataDictionaryElementName); 
    } // end setDataDictionaryElementName 

    public void validateObservation() { 
    /* Perform the validateObservation method. */ 
    getErrorLabel().setText(""); 
    setObservationValid(true); 
    setPanelObservationText( getObservationTextField().getText() ); 
    setObservationValid 
  (getDataElementObservation().isValid 
    (getPanelObservationText())); 
if( !getObservationValid() ) { 
    getErrorLabel().setText(getErrorText()); 
    } // end if 
    } // end validateObservation 
}end Observation Panel class 

// DataDictionary Record class an item in DD Vector 
public class DataDictionaryRecord { 
    private String dataElementName; 
    private Validator dataElementValidator; 
    private String min; 
    private String max; 
    private String tableName; 
    private java.util.Vector tableVector; 
    private String validatorName; 
    private int length; 
    private String labelName = new String(); 
    private String dataElementType; 
    private String invalidObservationLabel; 
} // end DDRecord class 
  

Listing 2 

// validator class 

public class Validator { 
    protected DataDictionaryRecord dDRecord; 
    public boolean isValid(String obs ) {   return false;} // end isValid 
} // end validator class 

// RangeValidator Class 
public class RangeValidator extends Validator { 
    public boolean isValid(String obs ) { 
    int obsInt; 
    if(getDDRecord().getDataElementType().equals("integer")) { 
       // validate that obs is an integer 
       // . 
       try {obsInt = Integer.parseInt( obs );} 
       catch(NumberFormatException e) {return false;} 
       // validate that obs meets the rules of min and max 
       int maxInt = Integer.parseInt(getDDRecord().getMax()); 
       int minInt = Integer.parseInt(getDDRecord().getMin()); 
       return (((minInt <= obsInt )&&(maxInt>=obsInt))); 
    } // end if 
    return false; 
    } // end isValid 
} // end rangevalidator 

  
 
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.