Template Method
Contents
Template Method
Template Method, a class behavioral pattern, provides the general steps of a method while deferring the implementation to its subclasses. Used to encapsulate algorithms, it can help reduce code duplication and maximizes the reuse of subclasses. Generally, an abstract base class is created, defining a template method of an algorithm. Later on, the subclasses can alter and implement the behavior.
UML Diagram
Source from GoF's Design Patterns - Elements of Reusable Object -Oriented Software (pg. 325)
Code Examples
Java
//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)); } }
Source from Javacamp, http://www.javacamp.org/designPattern/template.html/
PHP 5.0
abstract class AbstractClass { public final function templateMethod() { print "AbstractClass::templateMethod() called.\n"; $this->mandatoryOperation(); $this->optionalOperation(); } protected abstract function mandatoryOperation(); protected function optionalOperation() { } } class ConcreteClass extends AbstractClass { protected function mandatoryOperation() { print "ConcreteClass::mandatoryOperation() called.\n"; } protected function optionalOperation() { print "ConcreteClass::optionalOperation() called.\n"; } }
Source from Zend Technologies, http://www.zend.com/zend/php5/php5-OOP.php?article=php5-OOP&kind=ph&id=3204&open=1&anc=0&view=1
Real-life Application
Apache
/* * Copyright 1997-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.avalon.framework.component.test; import junit.framework.TestCase; import org.apache.avalon.framework.component.Component; import org.apache.avalon.framework.component.ComponentException; import org.apache.avalon.framework.component.DefaultComponentManager; /** * Test the basic public methods of DefaultComponentManager. * * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a> */ public final class DefaultComponentManagerTestCase extends TestCase { class DefaultRoleA implements Component,RoleA { public DefaultRoleA() { } } class DefaultRoleB implements Component,RoleB { public DefaultRoleB() { } } private DefaultComponentManager m_componentManager; protected boolean m_exceptionThrown; public DefaultComponentManagerTestCase() { this("DefaultComponentManager Test Case"); } public DefaultComponentManagerTestCase( final String name ) { super( name ); } protected void setUp() throws Exception { m_componentManager = new DefaultComponentManager(); m_exceptionThrown = false; } protected void tearDown() throws Exception { m_componentManager = null; } /** * lookup contract: * return first component found for role * search in hirarchy from current componentManager up. * if no compnent exist for role a in hierarchy * throw ComponentException */ public void testlookup1() throws Exception { DefaultRoleB roleBinBase = new DefaultRoleB(); DefaultRoleB roleBinParent = new DefaultRoleB(); DefaultRoleA roleAinParent = new DefaultRoleA(); m_componentManager.put(RoleA.ROLE,roleAinParent); m_componentManager.put(RoleB.ROLE,roleBinParent); DefaultComponentManager baseComponentManager = new DefaultComponentManager(m_componentManager); baseComponentManager.put(RoleB.ROLE,roleBinBase); Object lookupAinBase = baseComponentManager.lookup(RoleA.ROLE); Object lookupBinBase = baseComponentManager.lookup(RoleB.ROLE); Object lookupBinParent = m_componentManager.lookup(RoleB.ROLE); assertTrue( lookupAinBase instanceof RoleA); assertEquals( lookupBinBase, roleBinBase ); assertEquals( lookupBinParent, roleBinParent ); assertEquals( lookupAinBase,roleAinParent); } public void testlookup2() throws Exception { m_componentManager.put(RoleA.ROLE,new DefaultRoleA()); Object o = null; try { o = m_componentManager.lookup(RoleB.ROLE); } catch (ComponentException ce) { m_exceptionThrown = true; } if (o == null) assertTrue("ComponentException was not thrown when component was not found by lookup." ,m_exceptionThrown ); else assertTrue("component was found by lookup ,when there was no component.",false); } public void testhasComponent() throws Exception { m_componentManager.put(RoleA.ROLE,new DefaultRoleA()); assertTrue(m_componentManager.hasComponent(RoleA.ROLE)); assertTrue(!m_componentManager.hasComponent(RoleB.ROLE)); } public void testmakeReadOnly() throws Exception { //before read only m_componentManager.put(RoleA.ROLE,new DefaultRoleA()); Object a = m_componentManager.lookup(RoleA.ROLE); assertTrue( a instanceof RoleA); m_componentManager.makeReadOnly(); //after read only try { m_componentManager.put(RoleB.ROLE,new DefaultRoleB()); } catch (IllegalStateException se) { m_exceptionThrown = true; } assertTrue("IllegalStateException was not thrown in put after makeReadOnly." , m_exceptionThrown ); } }