Sponsored Link •
|
Summary
Template engines help separate presentation from domain logic. Presentation logic itself can be complex enough, however, to invite consideration of what should be placed in a template and what should be defined in separate classes. Template engines take different views on that point.
Advertisement
|
According to culinary historians, the first cookies were baked in Persia sometime around the 7th century A.D., serving as a kind of "test cake," allowing the baker to gauge the consistency of the dough. It is easy to imagine that the need to produce uniform-looking cookies soon brought about the first cookie cutters, a device that allowed the baker to neatly separate presentation from content.
A similar need to produce uniform-looking pages and documents, and to separate presentation from content, brought about the modern version of the cookie cutter, the template engine. In the 1980s and 90s, most users interacted with template engines by applying ready-made templates to word processing or presentation documents. Because templates were very simple instructions for layout and visual appearance, designers could focus on creating templates, and users with minimal technical skills could focus on applying those templates to their documents.
Such simplicity was soon eclipsed, however, by the need to produce increasingly complex visual presentations for Web pages. While the desire to separate data from presentation remained, complex visual layout invited more capable template logic, one where programming language code is sometimes used to affect presentation.
I recently looked at code from a project I worked on some months ago using Rails. I was pleasantly surprised to find that the controller code in this application is on average about a dozen or so lines of Ruby code per method, living up to Ruby's alleged simplicity. But the templates are a different story. In some cases, data had to be presented in such complex ways that the size of some of the templates far outstripped the amount of code used in the controllers. My aesthetic sense was disturbed by the complex templates, but I didn't think that alternative implementation techniques would have produced less complex code.
Although some of that complex presentation logic could have been factored out into presentation-related "helper" classes, doing that would not have reduced the complexity of the code, only moved the locus of complexity into other classes. In addition, splitting presentation logic between two classes (an RHTML template and a Ruby helper class) would have introduced a form of duplication, and diminished the overall clarity of the code base. Although the templates were complicated, the intention of the presentation logic remained clear to anyone looking at the template code.
In many cases, however, template engines that allow easy mixing of code with HTML or other presentation markup—be it JSP or RHTML or PHP— can clearly lead to hard-to-understand code. The evolution of Artima's own code base has illustrated this. Artima initially incorporated some proprietary JSP-based code. As the system grew, we extended that code with more functionality. However, such extensions were not easy, because the original JSP codebase consisted of a liberal mix of presentation and business logic.
The next step in the Artima code base was to introduce our own templates, initially based on the Velocity template library. This produced much cleaner presentation code, and we were at first happy with the result. However, as we looked back at some of the templates, it was hard to rejoice for long, since many of them were, in fact, mini programs: Velocity provides a very capable template language, and we found ourselves making good use of its features (such as making method calls into Java objects from the templates or defining complex branching logic).
We moved away from Velocity, and are now using StringTemplate. StringTemplate is unique in that it aims to strictly enforce the separation of content from code. In the words of its creator, Terence Parr (see the full Artima interview with Parr, Terence Parr on Strict Model-View Separation in Templates):
[We] came up with something very simpleāa document with holes in it. As needs dictated, I would gradually increase the power [of those documents] while sticking to my principle of absolutely enforcing strict model-view separation...
Encouraging something is infinitely inferior to enforcing [that thing]. If someone is in a hurry, all of a sudden someone can [do] something in the wrong way. If there is any way to enforce something, then you should do it...
At that time, we feared that [enforcing that] separation would emasculate the power of the template engine. I thought about that for a long time, and realized that it was sufficient to show a direct mapping from the hierarchy of nested templates that would, say, generate an HTML page, show a relationship between that and a parse tree or derivation tree from language theory...
There is a lot evidence that you don't need [to do] that much stuff in the view. In some cases it's clear, but there are many other cases where I'd argue that that computation needs to go into the model... You can't just multiply things in the view, you can't do array indexing, because that assumes that's something is an integer. What if an ID turns into a String next time? You want to try to isolate as much as you can.
Also, you can't have literals in your code... and you can't have any logic in your templates, like HTML in your code that is then passed in as String to the template...
Parr contrasted StringTemplate's philosophy with what he called the "pull model" of other template engines:
My rule is, you must compute everything a priori, and then push the values in [the template], and the template merely operates on those values. In the pull model, by contrast, you're actually pulling something from the model, and that's much more of a programming model than a view.
It turns out that, perhaps as a reaction to the generally messy state of JSP pages, such a clear separation philosophy is catching on. Among the more interesting full-stack Java application frameworks is RIFE. In a recent conversation, RIFE's creator, Geert Bevin told me that:
The template engine in RIFE is very different from what template engines have become over the years... In my opinion, template engines started out to isolate content out of your code and into separate files so that you don't have to put print statements inside your code. That's what I always thought of as templating. People have, over time, turned this into a language instead of just an aggregation of bits and pieces of content. So they started creating
if
statements, evenwhile
loops,for
loops, method calls, and then created new languages, such as PHP, that actually started out as a templating system.We go back to the roots, as I like to think about it. In RIFE, a template is totally dumb. It contains only content that you mark up with blocks and then you put place holders where content can be filled in. Blocks are automatically stripped away, so they don't show up for display, and show only when set inside place holders. To do layout and format your content, you drive that with Java by manipulating the content in a template instance. That's very similar to what you do when you work with PowerPoint, for example. In PowerPoint, you can use a template to create your own presentation. The template will not have logic to do that by itself. That's something you have to do through the application.
That's also how we see templating: You have your view layer that uses a template instance as some of kind layout and content building mechanism.
The clear separation of content from presentation sounds appealing at first. But, thinking back to my Rails templates of a few months ago, I was asking myself how I would have created the complex presentation logic that the project's users required, if I used a template engine that completely prohibited the use of programming language code. Is it really true that enforcing something is always better than simply encouraging a principle?
A natural antagonist to cut-and-dry rules, I'd rather observe two higher-level principles in judging how cleanly a template enforces model-view separation. The first principle is to look out for a special case of code duplication: splitting one logical piece of functionality between two or more classes or methods. Helper classes or methods are a useful way to factor out common pieces of functionality, but functionality not designed for reuse I prefer to define in a single place. Splitting complex layout into a helper class and a dumb template makes it more difficult to understand the code, because now you have to look at two places to grasp what happens. Thus, I think it is fine to place complex layout code in the template, even if that complex layout involves some algorithmic manipulation of data (such as highlighting alternate table rows, or to selectively show content based on the user's role).
The second principle is to try to minimize coupling of code, or what the Pragmatic Programmers call orthogonality. Having a companion class just because the template engine prevents even the smallest inclusion of algorithmic display code, foists coupling between the template and the companion class on the system: The template and the companion class cannot exist without each other, and changes in one almost always impacts the other.
While I am not hostile to a small amount of presentation-only code in templates, code that affects the state of domain objects crosses the line, in my view, into the territory of messy mixing of presentation and business logic. What I would like to see in a template engine is some kind of logic that strongly discourages—even prevents—presentation code to alter the state of domain objects.
What do you look for in a template engine? And where do you draw the line between clean separation of presentation from business logic code, on the one hand, and unacceptable mixing of the two, on the other?
Have an opinion? Readers have already posted 69 comments about this weblog entry. Why not add yours?
If you'd like to be notified whenever Frank Sommers adds a new entry to his weblog, subscribe to his RSS feed.
Frank Sommers is a Senior Editor with Artima Developer. Prior to joining Artima, Frank wrote the Jiniology and Web services columns for JavaWorld. Frank also serves as chief editor of the Web zine ClusterComputing.org, the IEEE Technical Committee on Scalable Computing's newsletter. Prior to that, he edited the Newsletter of the IEEE Task Force on Cluster Computing. Frank is also founder and president of Autospaces, a company dedicated to bringing service-oriented computing to the automotive software market.
Prior to Autospaces, Frank was vice president of technology and chief software architect at a Los Angeles system integration firm. In that capacity, he designed and developed that company's two main products: A financial underwriting system, and an insurance claims management expert system. Before assuming that position, he was a research fellow at the Center for Multiethnic and Transnational Studies at the University of Southern California, where he participated in a geographic information systems (GIS) project mapping the ethnic populations of the world and the diverse demography of southern California. Frank's interests include parallel and distributed computing, data management, programming languages, cluster and grid computing, and the theoretic foundations of computation. He is a member of the ACM and IEEE, and the American Musicological Society. |
Sponsored Links
|