Patterns 101: The Strategy Pattern

By Joe Hubert
Page 2 of 4

Example: My Restaurant Job Application Form

My friend has a small restaurant and would like me to write a web application that enables job applicants to submit information about them. The application will make sure each applicant has filled out enough information depending on the position for which he or she is applying.

The restaurant is accepting applications for these positions:

  • Manager
  • Wait staff
  • Busboy/Busgirl

The applicant form contains these fields:

  • Position (select from the list above)
  • Applicant Name
  • Phone Number
  • Email Address
  • Years experience
  • Reference 1 (text areas for entering contact information for the references)
  • Reference 2
  • Reference 3

Here are the basic rules for filling out the form:

  • For all applicants, the name, phone number, and the number of years of experience are required.
  • People applying for the manager position must include three personal references.
  • The wait staff and bus positions require one personal reference.

Before I write the web application, I'm going to create and test important components of the solution using Java. Let's look at some of the core classes:

Listing 1: JobApplicantForm.java
package strategy101.procedural;

import strategy101.common.FormSuccess;

public class JobApplicantForm {
    public static final int JOB_MANAGER = 1;
    public static final int JOB_WAIT_STAFF = 2;
    public static final int JOB_BUSSER = 3;

    private int position;
    private String name;
    private String phone;
    private String email;
    private Double yearsExp;
    private String reference1;
    private String reference2;
    private String reference3;

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Double getYearsExp() {
        return yearsExp;
    }

    public void setYearsExp(Double yearsExp) {
        this.yearsExp = yearsExp;
    }

    public String getReference1() {
        return reference1;
    }

    public void setReference1(String reference1) {
        this.reference1 = reference1;
    }

    public String getReference2() {
        return reference2;
    }

    public void setReference2(String reference2) {
        this.reference2 = reference2;
    }

    public String getReference3() {
        return reference3;
    }

    public void setReference3(String reference3) {
        this.reference3 = reference3;
    }

    public FormSuccess validate() /* to do: throws Exception */ {
        StringBuffer message = new StringBuffer();
        boolean success = true;
        int refCount = 0;

        //-- All applicants need to enter their name and phone number:
        if (isEmpty(name) || isEmpty(phone)) {
            success = false;
            message.append("\nPlease include your name and phone number.");
        }
        switch (position) {
            case JOB_MANAGER:
                if (yearsExp == null) {
                    success = false;
                    message.append("\nPlease enter your number years of experience.");
                }
                if (isEmpty(reference1) || isEmpty(reference2) || isEmpty(reference3)) {
                    success = false;
                    message.append("\nPlease enter three personal references and their contact information.");
                }
                break;

            case JOB_WAIT_STAFF:
                if (yearsExp == null) {
                    success = false;
                    message.append("\nPlease enter your number years of experience.");
                }
                if (isEmpty(reference1) && isEmpty(reference2) && isEmpty(reference3)) {
                    success = false;
                    message.append("\nPlease enter at least one personal reference and his/her contact information.");
                }
                break;

            case JOB_BUSSER:
                if (yearsExp == null) {
                    success = false;
                    message.append("\nPlease enter your number years of experience.");
                }
                if (isEmpty(reference1) && isEmpty(reference2) && isEmpty(reference3)) {
                    success = false;
                    message.append("\nPlease enter at least one personal reference and his/her contact information.");
                }
                break;

            default:
                // to do:
                //throw new Exception("Unregistered job type: " + position);
        }

        if (success) {
            message.append("\nThank you for submitting your job application to our restaurant.");
        }

        return new FormSuccess(success, message.toString());
    }
    private boolean isEmpty(String field) {
        return (field == null) || field.trim().equals("");
    }
}

This class represents the online information the applicant would enter. It could be considered a Java Bean but you don't need to be familiar with the concept of JavaBeans to understand this article.

Listing 2: JobApplicantTestClient.java
package strategy101.common;

import strategy101.procedural.JobApplicantForm;

public class JobApplicantTestClient {
    public static void main(String args[]) {

        JobApplicantForm form;
        FormSuccess result;

        //-- Negative test form for busboy/busgirl
        form = new JobApplicantForm();
        form.setPosition(JobApplicantForm.JOB_BUSSER);
        form.setPhone("111 222-3333");
        result = form.validate();
        runTest("Busboy/girl fail", result);

        //-- Positive test form for busboy/busgirl
        form = new JobApplicantForm();
        form.setPosition(JobApplicantForm.JOB_BUSSER);
        form.setName("Bobby Busser");
        form.setPhone("111 222-3333");
        form.setYearsExp(new Double(1));
        form.setReference1("Mr. Smith, 222-555-1234");
        result = form.validate();
        runTest("Busboy/girl pass", result);

        //-- Negative test form for wait staff
        form = new JobApplicantForm();
        form.setPosition(JobApplicantForm.JOB_WAIT_STAFF);
        form.setName("Wendy Waittress");
        form.setYearsExp(new Double(3));
        result = form.validate();
        runTest("Wait staff fail", result);

        //-- Positive test form for wait staff
        form = new JobApplicantForm();
        form.setPosition(JobApplicantForm.JOB_WAIT_STAFF);
        form.setName("Wendy Waittress");
        form.setPhone("111 222-4444");
        form.setYearsExp(new Double(3));
        form.setReference2("Mr. Jones, 222-555-2345");
        result = form.validate();
        runTest("Wait staff pass", result);


        //-- Negative test form for manager
        form = new JobApplicantForm();
        form.setPosition(JobApplicantForm.JOB_MANAGER);
        form.setName("Maxwell T. Manager");
        form.setReference1("Mr. Pink, 222-555-4567");
        result = form.validate();
        runTest("Manager fail", result);

        //-- Positive test form for manager
        form = new JobApplicantForm();
        form.setPosition(JobApplicantForm.JOB_MANAGER);
        form.setName("Maxwell T. Manager");
        form.setPhone("111 222-7777");
        form.setYearsExp(new Double(8));
        form.setReference1("Mr. Pink, 222-555-4567");
        form.setReference2("Mr. Bojangles, 222-555-2345");
        form.setReference3("Mr. Anderson, 222-555-6789");
        result = form.validate();
        runTest("Manager pass", result);
    }

    private static void runTest(String testName, FormSuccess result) {
        System.out.println("======= Running test: " + testName + " =======");
        if (result.isSuccess()) {
            System.out.println("Submit successful.");
        }
        else {
            System.out.println("Form did not pass validation. See message:");
        }
        System.out.println(result.getResultMessage());
        System.out.println("=======================\n");
    }
}

This class will create job applicant form objects and test the validation of data for each form.

Listing 3: FormSuccess.java
package strategy101.common;

public class FormSuccess {

    private boolean success= false;
    private String resultMessage = null;

    public FormSuccess(boolean success, String resultMessage) {
        this.success = success;
        this.resultMessage = resultMessage;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success= success;
    }

    public String getResultMessage() {
        return resultMessage;
    }

    public void setResultMessage(String resultMessage) {
        this.resultMessage = resultMessage;
    }
}

This class includes a success flag and a success or error message for the validation of each form submittal.

These classes are contained in the strategy101\common and strategy101\procedural packages in the source code available here. In my test client class, I create and validate some applicant forms. I run the test client from a command line and see the results of the validation. I have created a solution that meets my friend's requirements.

If you compile the source files and run JobApplicantTestClient, you can see the sample output.

The important business logic is contained within JobApplicantForm.java. This class might seem a logical home for this code for a lot of us. After all, shouldn't the form know how to validate itself?

Feedback

Articles
Factory 101

Strategy 101