The Phrasebook pattern is a technique to separate expressions in one language (e.g., SQL) from the main code that is written in another programming language (e.g., Perl). This is done by keeping the expressions in a separate file – the phrasebook. Any specific expression is generated by finding the appropriate entry in the phrasebook and substituting application parameters into it. This separation makes maintenance easier both for the main application code and for the foreign language code.
When the application needs to execute, create or output expressions in another language, keep these expressions in a separate phrasebook.
Consider a bookshop application written in Perl that uses a relational database and SQL. Users can search books by title, and the application will then display a list of books with this title. The books are indexed by their ISBN, so in order to display information for any of these books, the application needs to find its ISBN. This may be done by executing an SQL statement:
$statement = q(select isbn from book where title = 'Design Patterns'); $sth = $self->{DBH}->prepare($statement); $rc=$sth->execute();
The SQL code is embedded into the application code, usually in different places. This mix of two languages decreases the readability of the code and makes it harder to maintain both the application code and the SQL code. Also, similar SQL statements, perhaps differing only slightly, may appear in several places, thus making changes even more difficult. Moreover, the programmer that maintains the SQL code, needs to know Perl in order to find the SQL statements and to change them.
To avoid this, we can replace the inline SQL statements with calls to a PhraseGenerator
that is responsible for providing the necessary statements as shown in Figure 1. When a specific SQL statement is needed, we ask the PhraseGenerator
to generate it by specifying its phrase lookup key and parameters (if any):
$statement = $sql->get("FIND_ISBN", {title => "Design Patterns"}); $sth = $self->{DBH}->prepare($statement) ; $rc=$sth->execute();
We keep the actual SQL statements and their corresponding lookup keys in a separate phrasebook that is read and parsed by the PhraseGenerator
. We can use XML to wrap the SQL and the lookup keys:
... <statement name="FIND_ISBN"> select isbn from book where title = '$title' </statement> ...
The phrasebook entries may contain placeholders (like the $title
above) and simple control structures (if-else, while loops) that will be substituted with actual values when the statement is requested.
So instead of having SQL statements embedded on the main application code, we generate these statements from generic phrases.
get
method above doesn't provide type checking (that is not provided anyway by Perl). If we want to have type checking (in C++ for example) we can create a special method for each SQL statement instead of using only one method (get
) with keys:
statement = sql.find_isbn("Design Patterns");
In this case we need two classes, as shown in Figure 2: the first is the same PhraseGenerator
that we used above with its get
method; the second is a wrapper around it that provides one method for each generic phrase in the phrasebook (BookPhraseGenerator
).
Both these techniques allow us to provide custom SQL statements, without polluting the rest of our code with SQL.
Use this pattern when
Without static type checking:
With static type checking:
The SpecificPhraseGenerator object acts as an intermediate between the Client and the PhraseGenerator object. It performs the same operations as the Client in the typeless scenario. The collaboration between the SpecificPhraseGenerator object and the actual Client is as follows:
The pattern has the following advantages:
The pattern has the following liabilities:
$
":select isbn from book where title = '$title'
#define FIND_ISBN(t) select isbn from book where title = 't'
select isbn from book where title = '<title>Refactoring</title>'
<IF condition="no_matches"> Sorry, no matches found. </IF> <ELSE> <WHILE condition="next_match"> <A HREF="$address">$name</A><BR> </WHILE> </ELSE>
Listing 5 - Phrasebook entry with control structures
SQLStatement class is a PhraseGenerator Perl class that lets us manage centrally parameterized SQL statements.
use SQLStatement; # open the phrasebook my $sql = new SQLStatement("library.xml"); ... # generate an SQL statement $statement = $sql->get("FIND_ISBN", { title => $title });
The phrasebook is written in a separate file as XML document:
<?xml version="1.0"?> <!DOCTYPE sql [ <!ELEMENT sql (statement)*> <!ELEMENT statement (#PCDATA)> <!ATTLIST statement name CDATA #REQUIRED> ]> <sql> <!-- get the isbn according to the title --> <statement name="FIND_ISBN"> select isbn from book where title = '$title' </statement> ... </sql>
Each entry in the phrasebook has a name attribute that is used as a lookup key. The entries can specify parameters by using the $
escape character.
The SQLStatement class is used by EM-TECH group for several Web application projects. One example is the DHL Millenium Information Center that was used by DHL world wide in the first days of the millenium in order to have real time status reports from all over the world.
String tables, commonly used to make user interface easy to localize, are a simple instance of Phrasebook. The foreign language here is a human language (like English). The string table serves as a phrasebook: it contains string lookup keys and their associated string values. The string tables can be changed without affecting the main application code. When a specific message needs to be displayed, the application searches the string table for the string with the appropriate lookup key. Apple MacOS [2]), Microsoft Windows [3] and some application frameworks use string tables.
Many text editors have a user-configurable list of tools that can be invoked by the editor. Each tool is defined by a name and a shell command. The shell command may contain placeholders to parameters that are supplied at runtime by the editor application. For example, the Perl interpreter may be invoked on the active file by: perl %f
where %f
will be substituted with the current file name. The tool list is the phrasebook, and the shell command language is the foreign language.
Many code generators use a phrasebook with code snippets or complete source files. For example, Microsoft Visual C++ has a feature called Custom AppWizard [4] that generates source files from "TextTemplates". These templates can contain "macros" that are substituted with actual values when the files are generated. For example: class $$APP_CLASS$$
can be used to generate a class declaration. The class CCustomAppWiz
is the PhraseGenerator class, its member m_Dictionary
is the phrasebook, and its method ProcessTemplate
is used to generate the code phrases.
The Linux administration tool linuxconf [5] uses Phrasebook for localization. All the text for its user interface is placed in a phrasebook file. Each phrasebook entry may contain printf-like parameters. For example: Jobs listing for %s
.
The Skin pattern [1] guides to create a view specification language. Then it guides to use this language to create Skins that are templates for views. It can then use Phrasebook to access skins and generate views. In this case, each skin is a generic phrase in the view specification language of the application.
After the foreign language phrases are generated, they can be encapsulated inside Command objects [6].
Phrasebook can be used instead of Builder [6] if the product is in a foreign language (e.g. TeX or RTF) and the relations between its elements are simple (e.g., the product is simply an aggregation of its elements). Each element can be represented by a foreign language phrase. This allows more flexibilty and easier configurability, by using different phrasebooks instead of different subclasses of Builder. It also makes it easier to have complex elements, since these elements are maintained seperatly in the phrasebook.
Both these patterns separate the foreign language from the main code. Builder is better when the relations between foreign language phrases are complex, while Phrasebook is better when the phrases themselves are complex, as shown in Figure 5. When both the phrases and their relations are complex, it may be possible to use a combination of Builder and Phrasebook: The phrases are created using Phrasebook and assembled using Builder.
We would like to thank our PLoP2000 shepherd Alejandra Garrido for her insightful comments, and the writers' workshop participants for their helpful feedback.
We thank EM-TECH group for providing the environment where the SQLStatement class could be created, and for providing resources that helped in writing the pattern.
[1] Y. Sharon and R. Pinchuk. The Skin Pattern. PLoP 2000 Proceedings
[2] Apple Computer, Inc. Inside Macintosh: Macintosh Toolbox Essentials. Apple Developer Connection, 1996.
[3] MSDN Library. Platform SDK: Windows User Interface. Redmond, MA: Microsoft, 1998.
[4] MSDN Library. Visual C++ Programmer's Guide. Redmond, MA: Microsoft, 1998.
[5] C. Williams. Translation system for Linuxconf.
[6] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.