Working with Design Patterns: Template Method Los Angeles CA

Template Method is a design pattern that presents one solution for eliminating common duplication between two or more related classes. Work through an example and try it out yourself.

Local Companies

Moyea Software
92295612365
Hot building, ring street
LA, CA
Interneer Inc.
8005586832 x85
6101 W. Centinela Ave.
Culver City, CA
Cornerstone Concepts Inc
818-247-3909
600 W Broadway
Glendale, CA
Greene Computer Corporation
(818) 956-4961
200 S. Louise Street
Glendale, CA
Corticalx Inc Software Solutions & Technology
818-500-0881
425 E Colorado St
Glendale, CA
Alphatier Systems
818-409-8920
517 Griswold St
Glendale, CA
TimeTECH - Customizable Time and Attendance / Workforce Management Solutions
905-677-7009
7420 Airport Rd 203
Mississauga, CA
Hutchinson & Bloodgood, LLP
(818) 637-5000
101 N. Brand Blvd. #1600
Glendale, CA
Telsoft Solutions
818-545-8680
100 N Brand Blvd
Glendale, CA
Abraxas Technologies Inc
818-502-9100
450 N Brand Blvd
Glendale, CA

provided by: 
Originally published at Internet.com


Design patterns are powerful tools. Powerful tools always present the potential for misuse. If we're not careful, we could chop off a leg or glue our code together into a stick mess. Template Method is a design pattern that presents one solution for eliminating common duplication between two or more related classes. It is one of the twenty-three patterns demonstrated in the book Design Patterns (1995, by Gamma, et. al.).

You're best off treating Template Method as a refactoring goal. In this article, you'll step through an example that shows how to manipulate code to meet that goal. Instead of talking about what Template Method even is, we'll just refactor and talk about the results when we're done.

For this example, we're building a rule-based workflow system for managing scanned forms. The first rule we tackle requires that the state supplied on a form match the state passed in as an argument. The class CheckStateRule implements the Rule interface:

import java.util.*; public interface Rule { boolean eval(Case c, List val, boolean returnTrue); } The workflow system executes rules. Executing a rule can move forms through the workflow.

The CheckStateRule code is fairly straightforward. The rule engine passes a Case object, a list of arguments, and a negation flag to the eval method. The Case indirectly contains form data. The switchReturnValues method, a slightly confusing bit, flips the result of the rule if someone has configured it in the workflow system to be negated.

import java.util.*; /** * * Check if the state the case is located in is equal to argument passed in. */ public class CheckStateRule implements Rule { private static final String EXCEPT = "Exception: "; String errStr; boolean hasMissingData; public boolean eval(Case c, List val, boolean returnTrue) { boolean returnVal = false; String argVal = null; try { if (val == null || val.size() != 1) { errStr = "Can't convert value list to a state"; hasMissingData = true; return false; } else { argVal = (String)val.get(0); } // get the location object off the form and get the state the // case resides in Location loc = (Location)c.getFormComposite("location"); if (loc == null) { errStr = "Cannot determine the state this case is located in."; hasMissingData = true; return false; } String st = loc.getAttribute("state"); String state = (st == null ? null : st.toUpperCase()); if (state == null) { errStr = "Cannot determine the state this case is located in."; hasMissingData = true; return false; } else if (state.equals(argVal)) { returnVal = true; } // return value List returns = switchReturnValues(returnTrue, returnVal, "Case does not reside in '" + argVal + "'", "Case resides in '" + argVal + "'"); returnVal = ((Boolean)returns.get(0)).booleanValue(); errStr = (String)returns.get(1); } catch (Exception e) { errStr = EXCEPT + e.getMessage(); hasMissingData = true; return false; } return returnVal; } public List switchReturnValues(boolean returnTrue, boolean returnVal, String falseMsg, String trueMsg) throws Exception { if (!returnTrue) returnVal = !returnVal; String retErrStr = null; if (!returnVal) { if (returnTrue) retErrStr = falseMsg; else retErrStr = trueMsg; } ArrayList returns = new ArrayList(); returns.add(new Boolean(returnVal)); returns.add(retErrStr); return returns; } public boolean isMissingData() { return hasMissingData; } public String getError() { return errStr; } }

As soon as we get CheckStateRule working, we begin work on a second rule, CheckDependentsRule. The job of this second rule is to ensure that each dependent specified on the form has both a first and last name.

import java.util.*; /** * * Ensure all dependent detail is supplied */ public class CheckDependentsRule implements Rule { private static final String EXCEPT = "Exception: "; private String errorMessage; private boolean missingData = false; public boolean eval(Case c, List val, boolean returnTrue) { boolean returnVal = false; try { // get the location object off the form and get the state the // case resides in Subscriber sub = (Subscriber)c.getFormComposite("subscriber"); if (sub == null) { errorMessage = "Unable to obtain subscriber info."; missingData = true; return false; } returnVal = true; for (int i = 1; i <= Subscriber.DEPENDENT_ROWS; i++) { if (!hasCompleteDependentName(sub, i)) { returnVal = false; break; } } boolean returnVal1 = returnVal; if (!returnTrue) returnVal1 = !returnVal1; String retErrStr = null; if (returnVal1 == false) { if (returnTrue) retErrStr = "Dependent info incomplete"; else retErrStr = "All dependent info provided"; } List res = new ArrayList(); res.add(new Boolean(returnVal1)); res.add(retErrStr); // return value List returns = res; returnVal = ((Boolean)returns.get(0)).booleanValue(); errorMessage = (String)returns.get(1); } catch (Exception e) { errorMessage = EXCEPT + e.getMessage(); missingData = true; return false; } return returnVal; } private boolean hasCompleteDependentName(Subscriber sub, int number) { String firstName = sub.getAttribute("dep" + number + "first"); String lastName = sub.getAttribute("dep" + number + "last"); if ((!isEmpty(firstName) && isEmpty(lastName)) || (!isEmpty(lastName) && isEmpty(firstName))) { errorMessage = "Complete name must be specified for dependent " + number; return false; } return true; } private boolean isEmpty(String string) { return string == null || string.equals(""); } public boolean isMissingData() { return missingData; } public String getError() { return errorMessage; } }

The two rules, CheckDependentsRule and CheckStateRule, appear to have some similarities. Chunks of the eval method are the same, and a few supporting methods appear to be the same. We want to eliminate the redundancies between these two classes, which should reduce our risk and future maintenance costs.

Should we use delegation or inheritance? Normally I prefer to start by considering delegation, as it doesn't introduce as-strong dependencies. Delegation is ideal when we need to re-use common constructs and logic. But in this case, our two classes are behaviorally related. Both share the same goals and behaviors. We'll create a common class, BaseRule, and slowly pull common functionality up into it.

As the first step, we simply create an empty superclass and have both Rule classes extend it. We then, naturally, run our tests. public class BaseRule { } public class CheckStateRule extends BaseRule implements Rule { ... public class CheckDependentsRule extends BaseRule implements Rule { ...

If we look at CheckDependentsRule, the last chunk of code in the try block appears to replicate the functionality in CheckStateRule's switchReturnValues method.

boolean returnVal1 = returnVal; if (!returnTrue) returnVal1 = !returnVal1; String retErrStr = null; if (returnVal1 == false) { if (returnTrue) retErrStr = "Dependent info incomplete"; else retErrStr = "All dependent info provided"; } List res = new ArrayList(); res.add(new Boolean(returnVal1)); res.add(retErrStr); We extract this code to a separate method with the same name as in CheckStateRule: // return value List returns = switchReturnValues(returnTrue, returnVal, "Dependent info incomplete", "All dependent info provided"); ... } private List switchReturnValues(boolean returnTrue, boolean returnVal, String trueMsg, String falseMsg) { boolean returnVal1 = returnVal; if (!returnTrue) returnVal1 = !returnVal1; String retErrStr = null; if (returnVal1 == false) { if (returnTrue) retErrStr = trueMsg; else retErrStr = falseMsg; } List res = new ArrayList(); res.add(new Boolean(returnVal1)); res.add(retErrStr); return res; }

At this point we can visually inspect the two switchReturnValues methods and see if they indeed contain the same logic. Since we have good unit tests, we can spend only a little bit of time on this comparision, enough to get a good warm fuzzy. The real answer comes by trying to eliminate the redundancy: We pull one of them up to the BaseRule superclass (run tests), and eliminate the other (run tests again). All remains green.

import java.util.*; public class BaseRule { protected List switchReturnValues(boolean returnTrue, boolean returnVal, String trueMsg, String falseMsg) { boolean returnVal1 = returnVal; if (!returnTrue) returnVal1 = !returnVal1; String retErrStr = null; if (returnVal1 == false) { if (returnTrue) retErrStr = trueMsg; else retErrStr = falseMsg; } List res = new ArrayList(); res.add(new Boolean(returnVal1)); res.add(retErrStr); return res; } }

Getting rid of duplication often involves making things look the same. In both rule classes, we have a hasMissingData> flag. We also have a String field that appears to hold an error message, but its name is different. We rename the field in CheckStateRule from errStr to errorMessage (and run tests). We also make both fields private in CheckStateRule, since they are both private in CheckDependentsRule. We run our tests. We do the same thing with the hasMissingData/missingData fields.

Once we complete the preparatory work of making things look the same, we pull up both the fields and their accessor (getter) methods to BaseRule. We make the fields protected instead of private. This is perhaps a questionable design decision, but we can look to correct it once the rest of our current refactoring is complete. We verify that our tests still pass.

One more simple refactoring: both classes define an EXCEPT constant. We pull this definition to the superclass and eliminate from both subclasses.

Now the fun work begins. The eval method is still a bit long. But if we look at the structure of the eval method within the CheckStateRule, we note that it has the following policy: * extract arg values * extract form data * execute rule * prepare return values, negating if necessary * handle any trapped exception from the above three steps as a rule failure

CheckDependentsRule has pretty much the same structure, except that it doesn't extract any argument values. Starting with CheckStateRule, we break up the function into method calls that correspond to this policy.

import java.util.*; /** * * Check if the state the case is located in is equal to argument passed in. */ public class CheckStateRule extends BaseRule implements Rule { private String argVal; private String formState; public boolean eval(Case c, List val, boolean returnTrue) { try { return evalWithThrow(c, val, returnTrue); } catch (Exception e) { errorMessage = BaseRule.EXCEPT + e.getMessage(); hasMissingData = true; return false; } } private boolean evalWithThrow(Case c, List val, boolean returnTrue) { if (!extractArgs(val)) return false; if (!extractFormData(c)) return false; boolean returnVal = executeRule(c); return prepareReturnValues(returnTrue, returnVal); } private boolean extractFormData(Case c) { // get the location object off the form and get the state the // case resides in Location loc = (Location)c.getFormComposite("location"); if (loc == null) { errorMessage = "Cannot determine the state this case is located in."; hasMissingData = true; return false; } String st = loc.getAttribute("state"); formState = (st == null ? null : st.toUpperCase()); if (formState == null) { errorMessage = "Cannot determine the state this case is located in."; hasMissingData = true; return false; } return true; } private boolean prepareReturnValues(boolean returnTrue, boolean returnVal) { List returns = switchReturnValues(returnTrue, returnVal, "Case does not reside in '" + argVal + "'", "Case resides in '" + argVal + "'"); returnVal = ((Boolean)returns.get(0)).booleanValue(); errorMessage = (String)returns.get(1); return returnVal; } private boolean executeRule(Case c) { return formState.equals(argVal); } private boolean extractArgs(List val) { if (val == null || val.size() != 1) { errorMessage = "Can't convert

The methods eval and evalWithThrow provide the definitions of policy. Of course we make these changes incrementally. We also make typical simple mistakes along the way. Running the tests continuously keeps these mistakes from getting buried and becoming a big nuisance.

We can now pull up the eval and evalWithThrow policy methods to BaseRule. That creates complie errors. In order to get BaseRule compiling, we create stubs for things that are specific to CheckStateRule (for example, extractFormData). We change the stubs to protected in both BaseRule and CheckStateRule. Tests still run.

public boolean eval(Case c, List val, boolean returnTrue) { try { return evalWithThrow(c, val, returnTrue); } catch (Exception e) { errorMessage = BaseRule.EXCEPT + e.getMessage(); hasMissingData = true; return false; } } private boolean evalWithThrow(Case c, List val, boolean returnTrue) { if (!extractArgs(val)) return false; if (!extractFormData(c)) return false; boolean returnVal = executeRule(c); return prepareReturnValues(returnTrue, returnVal); } protected boolean prepareReturnValues(boolean returnTrue, boolean returnVal) { return false; } protected boolean executeRule(Case c) { return false; } protected boolean extractFormData(Case c) { return false; } protected boolean extractArgs(List val) { return false; }

In the next step, we convert the stub methods into abstract methods (which makes BaseRule abstract). Each such change causes compile errors in CheckDependentsRule. Rather than declare all methods abstract simultaneously, which would generate more than a few minutes work before we could see a green bar, we do them one at a time.

Finally, when done, we take a bold step and delete the eval method in CheckDependentsRule. Our resulting code:

import java.util.*; /** * * Ensure all dependent detail is supplied */ public class CheckDependentsRule extends BaseRule implements Rule { private Subscriber sub; @Override protected boolean prepareReturnValues(boolean returnTrue, boolean returnVal) { List returns = switchReturnValues(returnTrue, returnVal, "Dependent info incomplete", "All dependent info provided"); returnVal = ((Boolean)returns.get(0)).booleanValue(); errorMessage = (String)returns.get(1); return returnVal; } @Override protected boolean executeRule(Case c) { for (int i = 1; i <= Subscriber.DEPENDENT_ROWS; i++) { if (!hasCompleteDependentName(sub, i)) { return false; } } return true; } private boolean hasCompleteDependentName(Subscriber sub, int number) { String firstName = sub.getAttribute("dep" + number + "first"); String lastName = sub.getAttribute("dep" + number + "last"); if ((!isEmpty(firstName) && isEmpty(lastName)) || (!isEmpty(lastName) && isEmpty(firstName))) { errorMessage = "Complete name must be specified for dependent " + number; return false; } return true; } @Override protected boolean extractFormData(Case c) { sub = (Subscriber)c.getFormComposite("subscriber"); if (sub == null) { errorMessage = "Unable to obtain subscriber info."; hasMissingData = true; return false; } return true; } @Override protected boolean extractArgs(List&llt;String> val) { return true; } private boolean isEmpty(String string) { return string == null || string.equals(""); } }

The final version of BaseRule:

import java.util.*; abstract public class BaseRule { protected String errorMessage; protected boolean hasMissingData = false; static final String EXCEPT = "Exception: "; protected List switchReturnValues(boolean returnTrue, boolean returnVal, String trueMsg, String falseMsg) { boolean returnVal1 = returnVal; if (!returnTrue) returnVal1 = !returnVal1; String retErrStr = null; if (returnVal1 == false) { if (returnTrue) retErrStr = trueMsg; else retErrStr = falseMsg; } List res = new ArrayList(); res.add(new Boolean(returnVal1)); res.add(retErrStr); return res; } public boolean eval(Case c, List val, boolean returnTrue) { try { return evalWithThrow(c, val, returnTrue); } catch (Exception e) { e.printStackTrace(); errorMessage = BaseRule.EXCEPT + e.getMessage(); hasMissingData = true; return false; } } private boolean evalWithThrow(Case c, List val, boolean returnTrue) { if (!extractArgs(val)) return false; if (!extractFormData(c)) return false; boolean returnVal = executeRule(c); return prepareReturnValues(returnTrue, returnVal); } abstract protected boolean prepareReturnValues(boolean returnTrue, boolean returnVal); abstract protected boolean executeRule(Case c); abstract protected boolean extractFormData(Case c); abstract protected boolean extractArgs(List val); public boolean isMissingData() { return hasMissingData; } public String getError() { return errorMessage; } } And, for completeness sake, the final version of CheckStateRule: import java.util.*; /** * * Check if the state the case is located in is equal to argument passed in. */ public class CheckStateRule extends BaseRule implements Rule { private String argVal; private String formState; protected boolean extractFormData(Case c) { // get the location object off the form and get the state the // case resides in Location loc = (Location)c.getFormComposite("location"); if (loc == null) { errorMessage = "Cannot determine the state this case is located in."; hasMissingData = true; return false; } String st = loc.getAttribute("state"); formState = (st == null ? null : st.toUpperCase()); if (formState == null) { errorMessage = "Cannot determine the state this case is located in."; hasMissingData = true; return false; } return true; } protected boolean prepareReturnValues(boolean returnTrue, boolean returnVal) { List returns = switchReturnValues(returnTrue, returnVal, "Case does not reside in '" + argVal + "'", "Case resides in '" + argVal + "'"); returnVal = ((Boolean)returns.get(0)).booleanValue(); errorMessage = (String)returns.get(1); return returnVal; } protected boolean executeRule(Case c) { return formState.equals(argVal); } protected boolean extractArgs(List val) { if (val == null || val.size() != 1) { errorMessage = "Can't convert value list to a state"; hasMissingData = true; return false; }

Author: Jeff Langr

Read article at Internet.com site

Featured Local Company

Moyea Software

92295612365
Hot building, ring street
LA, CA

Related Local Events
Automation Technology Expo West (ATX West)
Dates: 2/9/2010 - 2/11/2010
Location: Anaheim Convention Center
Anaheim, CA
View Details

SOLAR POWER - Exhibition and Conference
Dates: 10/12/2010 - 10/14/2010
Location: Los Angeles Convention & Exhibition Center
Los Angeles, CA
View Details

REAL-TIME & EMBEDDED COMPUTING CONFERENCE - LONG BEACH 2009
Dates: 10/1/2009 - 10/1/2009
Location: Marriott Long Beach
Long Beach, CA
View Details

2009 IEEE Petroleum and Chemical Industry Technical Conference (PCIC 2009)
Dates: 9/14/2009 - 9/16/2009
Location:
Anaheim, CA
View Details

Medical Design & Manufacturing - Trade
Dates: 6/9/2009 - 6/11/2009
Location: CANON COMMUNICATIONS LLC
Los Angeles, CA
View Details