DRY (Don't Repeat Yourself) Design Patterns
One of the Pageforest applications I've been working on has prompted me to write a template generation language (modeled after Django's Template Language - DTL). DTL has a number of features that allow you to re-purpose templates and combine them in interesting ways. Yet, as a template language, it does not use the familiar concepts we, as programmers, are used to in our primary programming languages.
This got me thinking about the generalized patterns we use to help us design complex systems, by decomposing them into simpler parts. I'll compare DTL with JavaScript to show how the facilities in one comapare to those in the other.
-
Includes - DTL has a {% include "file.html" %} tag that can be used for basic composition of one template inside another. This is much like functional decomposition in a programming language; you can call a function from within another function:
function foo() {
bar();
}
But note that DTL does not support parameter substitution - or function arguments. This makes {% include %} a much less powerful and useful feature (and, in fact, is not much used in most Django applications). - Inheritance - DTL allows one template to extend another via the {% extends "base.html" %} tag. This turns out to be a very practical way to concisely design templates for different pages in an application, and yet enforce some regularity in the top-level design of the web site. Developers would typically create a master template that describes the top level layout and navigation for their site, and then individual site pages can extend that template, and replace sections with page-specific content.This is accomplished with {% block name %} tags. A child template can re-define any named block that appears in the template it extends. This is similar to inheritance-based composition in class-based languages. You can think of each block as a method in a base class, which can be selectively over-ridden by derived classes.
Yet, DTL is missing some important techniques for composition.
- Parameterized templates - As mentioned above, there is not method of invoking a template, but replacing formal parameters from a calling template. Variables in templates are basically dynamically scoped, so a calling template can define a variable that it knows will be used in an included template.
- Template as datatype - Just as we frequently create functions dynamically in JavaScript, and pass them around as data, it would be similarly useful to allow templates to be stored as data, passed in variables, and then invoked dynamically. There is not "eval" or "apply" function that would enable this type if template programming.
DTL is a very nice, and constrained, template definition language. And I like that fact that it does not expose the full complexity of a programming language to its users (which was it's creator's design philosophy). Yet it would be very useful to build language extensions via functional-like programming practices directly in the template language, without having to resort to escape functions that require writing custom tags and filters in Python.




