The Power of Template Design Pattern in JavaScript
Tags: javascript, webdev, node, programming
Published: 2022-04-23 22:42:31

![power-of-template-design-pattern-in-javascript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzk8x46450fjmx1d94x3.jpg)

If you've used nodejs before then you know that packages are at the heart of this platform. Every day and every second there is either a new update or a new package published to the npm registry. The majority of these packages are reusable and extensible. The way they do this can be one of many ways but there's one common trait that they all share: They can be seen as _templates_ that are waiting for _you_ to execute them.

This post will go over the Template Design Pattern in JavaScript. We will understand more in detail the approach of this pattern and _one_ scenario of _when_ we should use it. We will also see a diagram of how the the structure look like "outside the box". And finally, we will implement the pattern in code so that by the end of this article you will be comfortable about templating in JavaScript.

## How does the Template Pattern work?

When we are implementing this pattern a useful way to approach this is to think about the _start_ phase of something and the _end_ phase.

When we write functions the first thing we think about sometimes is to decide on its parameters and how variables will be initialized. Eventually we decide how to _end_ that function.

What happens in the middle depends on the implementation.

This is similar to how the flow of the Template works.

In more official terms it's essentially a bare interface that is given to the consumer where _they_ can implement one or more steps of the algorithm without changing the _structure_.

After they define those steps and follows execution, the "end" phase is reached, just like a basic function.

## When is the Template Pattern needed?

It's most needed in scenarios where two functions have important similarities in an implementation or interface but share the same problem where they aren't able to reuse those similarities. This means that when there is an update to one of the function's implementation, the _other_ function needs to update its implementation as well. This is a bad practice and eventually becomes unmaintainable if not dealt with.

This is where the Template Pattern comes in. It encapsulates those similarities in itself and delegates the responsibilities of the other parts out for those that derive and implement them themselves.

That way if there was a change to the implementation of the encapsulated parts all derived classes don't have to be involved in them.

## How does the Template Pattern look like in code?

In this section we will be implementing the Template Pattern ourselves.

Like I mentioned before, this can be implemented in a lot of ways because the pattern in its implementation is closely relative to the problem it is addressing. However they all have the same objective when we look at it in a bigger perspective.

Let's pretend we are building a function that runs a series of "transform" functions on a collection of dates _of any date format_. These can look like this:

```js
const dates = [
  357289200000,
  989910000000,
  'Tue Jan 18 2005 00:00:00 GMT-0800 (Pacific Standard Time)',
  new Date(2001, 1, 03),
  new Date(2000, 8, 21),
  '1998-02-08T08:00:00.000Z',
  new Date(1985, 1, 11),
  '12/24/1985, 12:00:00 AM',
  new Date(2020, 6, 26),
  'Tue May 15 2001 00:00:00 GMT-0700 (Pacific Daylight Time)',
  1652252400000,
  '2005-01-18T08:00:00.000Z',
  new Date(2022, 7, 14),
  '1999-02-01T08:00:00.000Z',
  1520668800000,
  504259200000,
  '4/28/1981, 12:00:00 AM',
  '2015-08-08T07:00:00.000Z',
]
```

Our function will implement the Template Pattern and our task is to define the base skeleton holding these _"empty"_ placeholders:

1. `reducer`
2. `transformer`
3. `finalizer`
4. `sorter`

When objects are created and derive from one of them they can provide their own algorithm that will be run when our function executes.

The consumer will have to implement the `reducer` as a function that takes an _accumulator_ and a _value_ and returns some accumulated result.

`transformer` is a function that transforms and returns a value of any data type.

`finalizer` takes in a value and also returns a value of any data type. But this time this value will be used to perform the final step.

The `sorter` is a function that takes in one item in the first argument and another item in the second argument. This function is the same as how you would implement the function in the native `.Array.sort` method.

Our function with the template implementation will be named `createPipeline` and takes in those functions if provided by the caller. If the caller doesn't provide one or more of them we must substitute them with a default implementation so that our algorithm can _still run_:

```js
function createPipeline(...objs) {
  let transformer
  let reducer
  let finalizer
  let sorter

  objs.forEach((o) => {
    const id = Symbol.keyFor(_id_)
    if (o[id] === _t) transformer = o
    else if (o[id] === _r) reducer = o
    else if (o[id] === _f) finalizer = o
    else if (o[id] === _s) sorter = o
  })

  if (!transformer) transformer = { transform: identity }
  if (!reducer) reducer = { reduce: identity }
  if (!finalizer) finalizer = { finalize: identity }
  if (!sorter) sorter = { sort: (item1, item2) => item1 - item2 }

  return {
    into(initialValue, ...items) {
      return items
        .reduce((acc, item) => {
          return reducer.reduce(
            acc,
            finalizer.finalize(transformer.transform(item)),
          )
        }, initialValue)
        .sort((item1, item2) => sorter.sort(item1, item2))
    },
  }
}
```

This simple function is a template where callers can pass in their own algorithms. It allows them to choose _not_ to pass in any implementation or allow them to pass in one or all of the 4 functions involved in the pipeline.

When they call the `into` function with a collection of items, the next step is to immediately run all of them through the pipeline and are eventually accumulated _into_ a new collection.

Something we often see from libraries that provide some form of template interface to consumers is that they try to make it as easy as possible to be worked with.

For example, the `createStore` in the redux library provides several overloads that developers can work with for instantiation. This is a very useful thing to do and it improves their reusability but also demonstrates the nature of a template in practice.

Inside template pattern implementations when there is a strict flow that an algorithm requires it is _usually hidden within the implementation_ like the `createStore` in redux.

When we go back to our previous example we noticed something in these lines:

```js
objs.forEach((o) => {
  const id = Symbol.keyFor(_id_)
  if (o[id] === _t) transformer = o
  else if (o[id] === _r) reducer = o
  else if (o[id] === _f) finalizer = o
  else if (o[id] === _s) sorter = o
})
```

This was not required or had anything to do with our pipeline _but_ because we created a helper to distinguish them we allowed the caller to pass in any of the `transformer`,` reducer`, `finalizer` and `sorter` functions in _any_ order _even though they need to be in order when it runs the functions_.

So any of these calls all return the same exact result even though they are ordered differently:

```js
console.log(getResult(reducer, transformer, finalizer, sorter))
console.log(getResult(transformer, reducer, finalizer, sorter))
console.log(getResult(finalizer, sorter, transformer, reducer))
console.log(getResult(sorter, finalizer, transformer, reducer))
```

In the internal implementation it doesn't work as expected if they were to be called in different orders because the sorter needs to be the final operation. The finalizer needs to be run _before_ the final (the sorter) operation and the transformer needs to be run _before_ the finalizer.

This is how the higher level implementation looks like:

```js
function createFactories() {
  const _id_ = Symbol.for('__pipeline__')
  const identity = (value) => value

  const factory = (key) => {
    return (fn) => {
      const o = {
        [key](...args) {
          return fn?.(...args)
        },
      }

      Object.defineProperty(o, Symbol.keyFor(_id_), {
        configurable: false,
        enumerable: false,
        get() {
          return key
        },
      })

      return o
    }
  }

  const _t = 'transform'
  const _r = 'reduce'
  const _f = 'finalize'
  const _s = 'sort'

  return {
    createTransformer: factory(_t),
    createReducer: factory(_r),
    createFinalizer: factory(_f),
    createSorter: factory(_s),
    createPipeline(...objs) {
      let transformer
      let reducer
      let finalizer
      let sorter

      objs.forEach((o) => {
        const id = Symbol.keyFor(_id_)
        if (o[id] === _t) transformer = o
        else if (o[id] === _r) reducer = o
        else if (o[id] === _f) finalizer = o
        else if (o[id] === _s) sorter = o
      })

      if (!transformer) transformer = { transform: identity }
      if (!reducer) reducer = { reduce: identity }
      if (!finalizer) finalizer = { finalize: identity }
      if (!sorter) sorter = { sort: (item1, item2) => item1 - item2 }

      return {
        into(initialValue, ...items) {
          return items
            .reduce((acc, item) => {
              return reducer.reduce(
                acc,
                finalizer.finalize(transformer.transform(item)),
              )
            }, initialValue)
            .sort((item1, item2) => sorter.sort(item1, item2))
        },
      }
    },
  }
}
```

One of several key parts of the internal implementation are these lines:

```js
Object.defineProperty(o, Symbol.keyFor(_id_), {
  configurable: false,
  enumerable: false,
  get() {
    return key
  },
})
```

This makes our template "official" because it hides the identifier from being seen from the outside and only exposes `createTransformer`, `createReducer`, `createFinalizer`, `createSorter`, and `createPipeline` to the consumer.

Another part that helps the template is the object above it:

```js
const o = {
  [key](...args) {
    return fn?.(...args)
  },
}
```

This helps to structure a _fluent_ api that reads like english:

```js
into(initialValue, ...items) {
	return items
		.reduce((acc, item) => {
			return reducer.reduce(
				acc,
				finalizer.finalize(transformer.transform(item)),
			)
		}, initialValue)
		.sort((item1, item2) => sorter.sort(item1, item2))
}
```

Lets pretend we are the consumer and we want to use this template on this collection of dates as we've seen earlier:

```js
const dates = [
  357289200000,
  989910000000,
  'Tue Jan 18 2005 00:00:00 GMT-0800 (Pacific Standard Time)',
  new Date(2001, 1, 03),
  new Date(2000, 8, 21),
  '1998-02-08T08:00:00.000Z',
  new Date(1985, 1, 11),
  '12/24/1985, 12:00:00 AM',
  new Date(2020, 6, 26),
  'Tue May 15 2001 00:00:00 GMT-0700 (Pacific Daylight Time)',
  1652252400000,
  '2005-01-18T08:00:00.000Z',
  new Date(2022, 7, 14),
  '1999-02-01T08:00:00.000Z',
  1520668800000,
  504259200000,
  '4/28/1981, 12:00:00 AM',
  '2015-08-08T07:00:00.000Z',
]
```

We have some issues:

1. They are in different data types. We want them all to be in _ISO date_ format.
2. They are not sorted. We want them all to be sorted in _ascending_ order.

We can use the code that implements the template design pattern to solve these issues so that we can get an ordered collection of dates in ISO format:

```js
const isDate = (v) => v instanceof Date
const toDate = (v) => (isDate(v) ? v : new Date(v))
const subtract = (v1, v2) => v1 - v2
const concat = (v1, v2) => v1.concat(v2)

const reducer = factory.createReducer(concat)
const transformer = factory.createTransformer(toDate)
const finalizer = factory.createFinalizer(toDate)
const sorter = factory.createSorter(subtract)

const getResult = (...fns) => {
  const pipe = factory.createPipeline(...fns)
  return pipe.into([], ...dates)
}

console.log(getResult(reducer, transformer, finalizer, sorter))
console.log(getResult(transformer, reducer, finalizer, sorter))
console.log(getResult(finalizer, sorter, transformer, reducer))
console.log(getResult(sorter, finalizer, transformer, reducer))
```

It doesn't require much code and all of our executions return the same result:

```json
[
  "1981-04-28T07:00:00.000Z",
  "1981-04-28T07:00:00.000Z",
  "1985-02-11T08:00:00.000Z",
  "1985-12-24T08:00:00.000Z",
  "1985-12-24T08:00:00.000Z",
  "1998-02-08T08:00:00.000Z",
  "1999-02-01T08:00:00.000Z",
  "2000-09-21T07:00:00.000Z",
  "2001-02-03T08:00:00.000Z",
  "2001-05-15T07:00:00.000Z",
  "2001-05-15T07:00:00.000Z",
  "2005-01-18T08:00:00.000Z",
  "2005-01-18T08:00:00.000Z",
  "2015-08-08T07:00:00.000Z",
  "2018-03-10T08:00:00.000Z",
  "2020-07-26T07:00:00.000Z",
  "2022-05-11T07:00:00.000Z",
  "2022-08-14T07:00:00.000Z"
]
```

Here is a diagram depicting our template:

![template-design-pattern-javascript-diagram.png](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/power-of-template-design-pattern-in-javascript/template-design-pattern-javascript-diagram.png 'Template Design Pattern Javascript Diagram Png')

And there you go!

## Another Example

I like to use [snabbdom](https://github.com/snabbdom/snabbdom) to demonstrate concepts in several of my posts because it is short, simple, powerful and uses several techniques that are relative to the topics I wrote about in the past. Snabbdom is a front end JavaScript library that lets you work with a virtual DOM to create robust web applications. They focus on simplicity, modularity and performance.

They provide a module api where developers can create their own modules. They do this by providing to consumers a template that provides _hooks_ that hook onto the lifecycle of a "patching" phase where DOM elements are passed around to life cycles. This is a simple but powerful way to work with the virtual DOM. It is a great example of one variation of a template pattern.

This is their template:

```ts
const myModule = {
  // Patch process begins
  pre() {
    //
  },
  // DOM node created
  create(_, vnode) {
    //
  },
  // DOM node is being updated
  update(oldVNode, vnode: VNode) {
    //
  },
  // Patching is done
  post() {
    //
  },
  // DOM node is being directly removed from DOM via .remove()
  remove(vnode, cb) {
    //
  },
  // DOM node is being removed by any method including removeChild
  destroy(vnode) {
    //
  },
}
```

## Conclusion

And that concludes the end of this post! I hope you got something out of it and look out for more posts from me in the future!