Definition
Provide an abstract definition for a method or a class and redefine its behavior later or on the fly without changing its structure.Intent
01- Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
- Base class declares algorithm ‘placeholders’, and derived classes implement the placeholders.
Structure
06
The implementation of
template_method()
is: call step_one()
, call step_two()
, and call step_three()
. step_two()
is a “hook” method – a placeholder. It is declared in the base class, and then defined in derived classes. Frameworks (large scale reuse infrastructures) use Template Method a lot. All reusable code is defined in the framework’s base classes, and then clients of the framework are free to define customizations by creating derived classes as needed.Problem
02Two different components have significant similarities, but demonstrate no reuse of common interface or implementation. If a change common to both components becomes necessary, duplicate effort must be expended.
Where to use & benefits
- To make many similar operations template.
- From many specialized operations to a generalized operation.
- Refactor common behavior to simplify code.
- Algorithm related improvement.
- Need good coding skill to conquer it.
- May be hard to read for novice.
- Easy to produce ambiguity if not written well.
- Related patterns include
- Factory Method, which is combined with template method.
- Strategy, which is used to delegate or coordinate the template method.
Motivation
If we take a look at the dictionary definition of a template we can see that a template is a preset format, used as a starting point for a particular application so that the format does not have to be recreated each time it is used.
On the same idea is the template method is based. A template method defines an algorithm in a base class using abstract operations that subclasses override to provide concrete behavior.
Intent
- Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
- Template Method lets subclasses redefine certain steps of an algorithm without letting them to change the algorithm's structure.
Implementation
AbstractClass - defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm.
- implements a template method which defines the skeleton of an algorithm. The template method calls primitive operations as well as operations defined in AbstractClass or those of other objects.
ConcreteClass - implements the primitive operations to carry out subclass-specific steps of the algorithm.
When a concrete class is called the template method code will be executed from the base class while for each method used inside the template method will be called the implementation from the derived class.
Examples
For example, a loan application process may take several steps to finish. Let's assume the steps are as follows: - check client's bank balance history
- check client's credit score from three different companies
- check client's other loan information
- check client's stock holding value
- check client's income potential in the future
- etc.
abstract class CheckBackground { public abstract void checkBank(); public abstract void checkCredit(); public abstract void checkLoan(); public abstract void checkStock(); public abstract void checkIncome(); //work as template method public void check() { checkBank(); checkCredit(); checkLoan(); checkStock(); checkIncome(); } } class LoanApp extends CheckBackground { private String name; public LoanApp(String name) { this.name = name; } public String getName() { return name; } public void checkBank() { //ck acct, balance System.out.println("check bank..."); } public void checkCredit() { //ck score from 3 companies System.out.println("check credit..."); } public void checkLoan() { //ck other loan info System.out.println("check other loan..."); } public void checkStock() { //ck how many stock values System.out.println("check stock values..."); } public void checkIncome() { //ck how much a family make System.out.println("check family income..."); } //other methods } class TestTemplate { public static void main(String[] args) { LoanApp mortgageClient = new LoanApp("Judy"); System.out.println("\nCheck client " + mortgageClient.getName()+ " Mortgage loan application. "); mortgageClient.check(); LoanApp equityloanClient = new LoanApp("Mark"); System.out.println("\nCheck client " + equityloanClient.getName()+ " equity loan application. "); equityloanClient.check(); } }C:\ Command Prompt
C:\> javac TestTemplate.java
C:\> java TestTemplate
Check client Judy Mortgage loan application.
check bank...
check credit...
check other loan...
check stock values...
check family income...
Check client Mark equity loan application.
check bank...
check credit...
check other loan...
check stock values...
check family income...
C:\> |
Method overloading and method overriding are good examples of template method pattern. For example,
- Coercion polymorphism -- refers to a single operation serving several types through implicit type conversion.
- Overloading polymorphism -- refers to using a single identifier for different operations.
- Parametric polymorphism -- refers to a class declaration that allows the same field names and method signatures to associate with a different type in each instance of that class.
//coercion polymorphism abstract class Add { public abstract double add(double d1, double d2);//template } class AddAnyTypeNumber extends Add{ public double add(double d1, double d2) { return d1 + d2; } } class Test { public static void main(String[] args) { double d1 = 10.5, d2 = 9.5; float f1 = 11.5f, f2 = 12.5f; long l1 = 1, l2 = 2; int i1 = 3, i2 = 4; short s1 = 7, s2 = 8; byte b1 = 5, b2 = 6; AddAnyTypeNumber addNumber = new AddAnyTypeNumber(); System.out.println(addNumber.add(d1,d2)); System.out.println((float)addNumber.add(f1,f2)); System.out.println((long)addNumber.add(l1,l2)); System.out.println((int)addNumber.add(i1,i2)); System.out.println((short)addNumber.add(s1,s2)); System.out.println((byte)addNumber.add(b1,b2)); } }C:\ Command Prompt
C:\> java Test
20.0
24.0
3
7
15
11 |
If you don't have template method pattern concept or don't know Java type promotion technique, you may write code in the following way:
abstract class Add { public abstract double add(double d1, double d2); public abstract float add(float d1, float d2); public abstract long add(long d1, long d2); public abstract int add(int d1, int d2); public abstract short add(short d1, short d2); public abstract byte add(byte d1, byte d2); } class AddNumber extends Add{ public double add(double d1, double d2) { return d1 + d2; } public float add(float f1, float f2) { return f1 + f2; } public long add(long l1, long l2) { return l1 + l2; } public int add(int i1, int i2) { return i1 + i2; } public short add(short s1, short s2) { return (short)(s1 + s2); } public byte add(byte b1, byte b2) { return (byte)(b1 + b2); } } class Test { public static void main(String[] args) { double d1 = 10.5, d2 = 9.5; float f1 = 11.5f, f2 = 12.5f; long l1 = 1, l2 = 2; int i1 = 3, i2 = 4; short s1 = 7, s2 = 8; byte b1 = 5, b2 = 6; AddNumber addNumber = new AddNumber(); System.out.println(addNumber.add(d1,d2)); System.out.println(addNumber.add(f1,f2)); System.out.println(addNumber.add(l1,l2)); System.out.println(addNumber.add(i1,i2)); System.out.println(addNumber.add(s1,s2)); System.out.println(addNumber.add(b1,b2)); } }C:\ Command Prompt
C:\> java Test
20.0
24.0
3
7
15
11 |
Before
01The SortUp and SortDown classes are almost identical. There is a massive opportunity for reuse - if - the embedded comparison could be refactored.
02class SortUp
{
// Shell sort
public:
void sort(int v[], int n)
{
for (int g = n / 2; g > 0; g /= 2)
for (int i = g; i < n; i++)
for (int j = i - g; j >= 0; j -= g)
if (v[j] > v[j + g])
doSwap(v[j], v[j + g]);
}
private:
void doSwap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
};
class SortDown
{
public:
void sort(int v[], int n)
{
for (int g = n / 2; g > 0; g /= 2)
for (int i = g; i < n; i++)
for (int j = i - g; j >= 0; j -= g)
if (v[j] < v[j + g])
doSwap(v[j], v[j + g]);
}
private:
void doSwap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
};
int main()
{
const int NUM = 10;
int array[NUM];
srand((unsigned)time(0));
for (int i = 0; i < NUM; i++)
{
array[i] = rand() % 10+1;
cout << array[i] << ' ';
}
cout << '\n';
SortUp upObj;
upObj.sort(array, NUM);
for (int u = 0; u < NUM; u++)
cout << array[u] << ' ';
cout << '\n';
SortDown downObj;
downObj.sort(array, NUM);
for (int d = 0; d < NUM; d++)
cout << array[d] << ' ';
cout << '\n';
system("pause");
}
3 10 5 5 5 4 2 1 5 9 1 2 3 4 5 5 5 5 9 10 10 9 5 5 5 5 4 3 2 1
After
03The common implementation has been moved to an abstract base class, and a “placeholder” has been defined to encapsulate the embedded comparison.
04 All that remains for the
05SortUp
and SortDown
classes is to implement that placeholder.class AbstractSort
{
// Shell sort
public:
void sort(int v[], int n)
{
for (int g = n / 2; g > 0; g /= 2)
for (int i = g; i < n; i++)
for (int j = i - g; j >= 0; j -= g)
if (needSwap(v[j], v[j + g]))
doSwap(v[j], v[j + g]);
}
private:
virtual int needSwap(int, int) = 0;
void doSwap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
};
class SortUp: public AbstractSort
{
/* virtual */
int needSwap(int a, int b)
{
return (a > b);
}
};
class SortDown: public AbstractSort
{
/* virtual */
int needSwap(int a, int b)
{
return (a < b);
}
};
int main()
{
const int NUM = 10;
int array[NUM];
srand((unsigned)time(0));
for (int i = 0; i < NUM; i++)
{
array[i] = rand() % 10+1;
cout << array[i] << ' ';
}
cout << '\n';
AbstractSort *sortObjects[] =
{
new SortUp, new SortDown
};
sortObjects[0]->sort(array, NUM);
for (int u = 0; u < NUM; u++)
cout << array[u] << ' ';
cout << '\n';
sortObjects[1]->sort(array, NUM);
for (int d = 0; d < NUM; d++)
cout << array[d] << ' ';
cout << '\n';
system("pause");
}
1 6 6 2 10 9 4 10 6 4 1 2 4 4 6 6 6 9 10 10 10 10 9 6 6 6 4 4 2 1
No comments:
Post a Comment