Design mode - template method mode ~ cold dew

Posted by jbog91 on Tue, 11 Jan 2022 16:54:28 +0100

Template Method Pattern

TitleModuleCategoryTags
Template Method
template-design
Behavior
Gang of Four

Introduction to template method mode

Template method pattern refers to defining the algorithm framework behavior in an operation and delaying some steps to the subclass for implementation, so that the subclass can redefine some specific steps of the algorithm without changing an algorithm structure. It belongs to behavioral design pattern Template pattern essentially encapsulates a fixed process, which consists of several steps, and the specific steps are implemented by subclasses In fact, it is the inheritance mechanism of class, abstract encapsulation process and concrete implementation of subclasses

Structure diagram

The role of the template method pattern is relatively simple, involving two types of roles:

  • Abstract template: abstract template, which defines a set of algorithm framework / process
  • Concretetemplate: a concrete implementation class that implements some steps of the algorithm process framework

Application scenario

  • We need to implement the immutable part of an algorithm and leave the variable behavior part to the subclass implementation
  • The common behaviors in each subclass are abstracted and concentrated in the public abstract class to avoid code duplication

Code example

The traditional practice cases of template pattern are relatively simple. You can see the use cases here Here

Open source application

  • Template pattern in Spring

Spring often sees the tempalt framework, but in most cases, instead of using the traditional template method mode, it adopts the form of CallBack function to avoid the inheritance structure. At the same time, each class can implement its own specific functions separately:

Take the StringRedisTemplate in RedisTempalte as an example. Here, call the afterpropertieset() of the parent class:

public class StringRedisTemplate extends RedisTemplate<String, String> {

   /**
    * Constructs a new <code>StringRedisTemplate</code> instance. 
    * {@link 		#setConnectionFactory(RedisConnectionFactory)}
    * and {@link #afterPropertiesSet()} still need to be called.
    */
   public StringRedisTemplate() {
      setKeySerializer(RedisSerializer.string());
      setValueSerializer(RedisSerializer.string());
      setHashKeySerializer(RedisSerializer.string());
      setHashValueSerializer(RedisSerializer.string());
   }

   /**
    * Constructs a new <code>StringRedisTemplate</code> instance ready to be used.
    *
    * @param connectionFactory connection factory for creating new connections
    */
   public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
      this();
      setConnectionFactory(connectionFactory);
      afterPropertiesSet();
   }

   protected RedisConnection preProcessConnection( 
   RedisConnection connection, boolean existingConnection) {
      return new DefaultStringRedisConnection(connection);
   }
}

The same way inherited by the parent class calls afterProperteisSet() in the following method, and refers to the parent class by super:

public class RedisTemplate<K, V> extends RedisAccessor 
    implements RedisOperations<K, V>, BeanClassLoaderAware {


   /**
    * Constructs a new <code>RedisTemplate</code> instance.
    */
   public RedisTemplate() {}

   /*
    * (non-Javadoc)
    * @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet()
    */
   @Override
   public void afterPropertiesSet() {

      super.afterPropertiesSet();

      boolean defaultUsed = false;

      if (defaultSerializer == null) {

         defaultSerializer = new JdkSerializationRedisSerializer(
               classLoader != null ? classLoader : this.getClass().getClassLoader());
      }

      if (enableDefaultSerializer) {

         if (keySerializer == null) {
            keySerializer = defaultSerializer;
            defaultUsed = true;
         }
         if (valueSerializer == null) {
            valueSerializer = defaultSerializer;
            defaultUsed = true;
         }
         if (hashKeySerializer == null) {
            hashKeySerializer = defaultSerializer;
            defaultUsed = true;
         }
         if (hashValueSerializer == null) {
            hashValueSerializer = defaultSerializer;
            defaultUsed = true;
         }
      }

      if (enableDefaultSerializer && defaultUsed) {
         Assert.notNull(defaultSerializer,
          "default serializer null and not all serializers initialized");
      }

      if (scriptExecutor == null) {
         this.scriptExecutor = new DefaultScriptExecutor<>(this);
      }

      initialized = true;
   }
}

When referencing afterpropertieset() in the parent redisacceptor class, the subclass can freely configure the connection factory or directly use the default implementation of the parent class by defining the getConnectionFactory() method (Spring provides an extension interface such as InitializingBean)

public class RedisAccessor implements InitializingBean {

   /** Logger available to subclasses */
   protected final Log logger = LogFactory.getLog(getClass());

   private @Nullable RedisConnectionFactory connectionFactory;

   public void afterPropertiesSet() {
      Assert.state(getConnectionFactory() != null, "RedisConnectionFactory is required");
   }
}

Thinking summary

The hook function provided by the template method is cleverly implemented by defining abstract methods and delaying them to subclasses. It conforms to the "open close" principle. Skillfully using the template method is conducive to building more flexible programming and defining a variety of algorithm framework processes

advantage
  • The template method is used to extract the code with the same logic into the abstract parent class to improve the reusability of the code
  • By extending subclasses to add new behaviors, code scalability is improved
shortcoming
  • As the number of classes increases, each abstract class needs to be implemented by subclasses, resulting in an increase in the number of classes
  • It may increase the complexity of system implementation
  • Due to the shortcomings of inheritance, the modification of the abstract parent class and the addition of new abstract methods will increase the number of subclasses involved

Reference

  • E.Gamma, R.Helm, R.Johnson, and Vlissides. Design Patterns Elements of Reusable Object Oriented Software. Addison-Wesley, 1995
  • Template Method Pattern on Wiki

Topics: Design Pattern