The Skin Pattern is a technique to separate the presentation style of an application (its "skin") from its logic. Keeping the skins separate makes the application code closed to cosmetic changes in the UI, and the UI closed to bug fixes and changes in the application code. The code becomes more readable and easier to maintain. Further, the people who define and maintain the skins do not need to understand any programming language, since there is no code in the skins. Graphic tools may be used to generate and modify the skins, thus providing intuitive and friendly tools for UI designers.
Separate the presentation style of an application from its logic.
Web applications dynamically create HTML pages. One common technique to do this is to insert HTML code inside the application code, sometimes using special method calls, as shown in listings 1-2.
Another common technique is to write the application within the HTML pages by inserting program instructions in the HTML code, as shown in listings 3-4. When the application is run, the program code and the HTML code are read together, and generate the final HTML pages.
... totalPrice = price * qty; htmlHeading h1( 1 ); h1 << "The total price is" << totalPrice << "$";
... $total_price = $price * $qty; $cgi->h1("The total price is $total_price\$");
<CFSET total_price = (#qty# * #price#)> <CFOUTPUT> <H1>The total price is #total_price#$</H1> </CFOUTPUT>
<%... total_price = qty * prices %> <H1>The total price is <%=total_price%>$</H1>
Both techniques cause a situation where the application code suffers in its readability and its maintainability: It is difficult to see the logic of the application when the presentation code is mixed inside. Moreover, usually the programmer is not responsible for the look of the application, and the graphic designer will not want to see the program code.
We can separate the logic of the application from its presentation style by introducing skins as shown in Figure 1. In the Web application example the skins will be pages written in HTML with some simple extensions that work mainly as placeholders for application-supplied data. A Skin object will parse one of those pages, and then the application can use the Skin object to generate the output HTML by supplying the actual data to fill in the placeholders. Note that although we add extensions to the HTML code, they are only used for displaying application-supplied data, not for manipulating that data. This way the application logic does not creep into the HTML page.
The skin page defines a general HTML page, while the Skin object enables the application to generate a specific HTML page with specific data.
Use this pattern when
The pattern deals only with the generation of these views. Specifying interactive behavior of the views after they are created is orthogonal to using Skin for view generation.
Implementation considerations regarding the specification language of the skins (hereafter the language):
<h1>$title</h1>
). At the other extreme is a Turing complete language. We should try to keep the language as close as possible to the first extreme, since the second extreme allows application logic to creep into the skin, and this contradicts the intent of this pattern.The total price is $price$
); orThe total price is <a name="price">0.00</a>$
).Implementation considerations regarding the application code:
The following example shows a part of an application that generates an HTML page that presents a list of items in a table. Clicking on the title of one of the columns will sort the table according to that column:
id | name |
2 | Boaz |
3 | Gull |
1 | Michael |
The Skin specification for this page is as follows:
<HTML> <HEAD> <TITLE> $title </TITLE> </HEAD> <BODY> <H1> $title <H1> <TABLE border="1"> <TR> <TD> <IF condition="is_order_id">id</IF> <ELSE><A HREF="order.pl?order=id">id</A></ELSE> </TD> <TD> <IF condition="is_order_name">name</IF> <ELSE><A HREF="order.pl?order=name">name</A></ELSE> </TD> <WHILE condition="user_list"> <TR><TD>$id</TD><TD>$name</TD></TR> </WHILE> </TABLE> </BODY> </HTML>
The code that uses this Skin specification to show the above view is as follows:
#!/usr/local/bin/perl use TextTemplate; use CGI; # a list of sample users: my @List = ( { id => 1, name => "Michael" }, { id => 2, name => "Boaz" }, { id => 3, name => "Gull" } ); my $title = "Sample Users List with Order"; # the page title my $i = 0; # the counter i of the list - we start with 0 my $id; # the id of the current row my $name; # the name of the current row # the sorting order: my $cgi = new CGI; # create the CGI object print $cgi->header(); # print the HTTP header my $order = $cgi->param("order") || "id"; # the default order is by "id" # create new TextTemplate object my $template = new TextTemplate("users_list.tmp"); # read the template, parse it and print the generated results. we send # in an anonymous hash all the variables that we would like to use in # the template, or that we should use in the call back function. print $template->generate({ title => $title, order => $order, is_order_id => $order eq "id", is_order_name => $order eq "name", i => $i, id => $id, name => $name, user_list => \&list_call_back_func }); # update $id and $name to the next item in the list sub list_call_back_func { my $variables = shift; # always a reference to the anonymous hash # that we send with the parse method is # send to the callback functions. my $i = $variables->{i}; # the counter if ($i < scalar(@List)) { # create an ordered list from the global list my @list; if ($order eq "id") { @list = sort {$a->{id} <=> $b->{id}} @List; } elsif ($order eq "name") { @list = sort {$a->{name} cmp $b->{name}} @List; } # get the name and the id for that counter value from the # global List, when the list is ordered by the order. $variables->{id} = $list[$i]->{id}; $variables->{name} = $list[$i]->{name}; $variables->{i}++; # increment the value of id return 1; # continue to loop } else { return 0; # we finished } }
The MFC GUI framework [6] uses skins for dialog boxes. The skins are called " dialog templates" and are parsed by objects of class CDialog that serves as the Skin class. Here is a simple template for the "Message of the Day" dialog box shown in Figure 4:
The corresponding dialog template is:
IDD_MOTD_DIALOG DIALOGEX 0, 0, 196, 113 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW CAPTION "Message of the Day" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,73,92,50,14 LTEXT "date",IDC_DATE,7,7,182,8 EDITTEXT IDC_MESSAGE,7,24,182,59,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP END
IDD_MOTD_DIALOG
), its type (DIALOGEX
) and coordinates. It is followed by lines specifying attributes of this dialog box and a BEGIN-END block that specifies its contents: a button, a static text area labeled IDC_DATE
(for the date), and an editable text area labeled IDC_MESSAGE
(for the message).
To create a dialog box using this template, all we need to do is:
CDialog dlg(IDD_MOTD_DIALOG); Dlg.DoModal();
However, this will not allow us to manipulate the values of the dialog contents (specifically, the date and message). To do this, we derive a new class from CDialog
, that will serve as the Client:
class CMotdDlg : public CDialog { public: CMotdDlg() : CDialog(IDD_MOTD_DIALOG) {} protected: virtual BOOL OnInitDialog(); // ... };
The OnInitDialog()
member function is where we put the application supplied data into the date and message parts of the dialog box:
BOOL CMotdDlg::OnInitDialog() { CDialog::OnInitDialog(); SetDlgItemText(IDC_DATE, theApp.GetDay()); SetDlgItemText(IDC_MESSAGE, theApp.GetMessage()); return TRUE; }
We can easily change the appearance of the dialog box without touching application code. For example, to make the dialog box look like Figure 5
we changed its template to:
IDD_MOTD_DIALOG DIALOGEX 0, 0, 182, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION EXSTYLE WS_EX_APPWINDOW CAPTION "Message of the Day" FONT 10, "Times New Roman" BEGIN DEFPUSHBUTTON "Close",IDOK,125,72,50,14 LTEXT "date",IDC_DATE,7,72,107,14 EDITTEXT IDC_MESSAGE,7,7,168,59,ES_CENTER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP END
The dialog templates themselves are usually manipulated using graphic editors, so the user interface designers do not even need to learn the dialog templates format.
The TextTemplate class (shown in the Sample Code section) 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.
Both Apple MacOS [7] and Microsoft Windows [8] use skins for dialog boxes. The skins are called dialog templates and are specified in resource files (several skins can be defined in one resource file).
Many open source projects implement the skin pattern, among them:
XSL [9] can be used to specify skins. The generated views are in XML or HTML. The XSL processor acts as the Skin object. The client generates documents by marshalling the view-specific data to an XML document and invoking the XSL processor on this document.
Sometimes the similarities between different views are not simple, and so a common structure cannot be easily formulated. In these cases it may be better to construct views from common elements using Builder [10] – where each view is constructed element-by-element, or using Phrasebook [11] – where each element is an expression in the foreign language phrasebook.
The Skin can use the Phrasebook pattern [11] to access the skins from the application code. The skin specification language is the foreign language and each skin is equivalent to a foreign language phrase.
The View part of MVC [12] or Model-View can use a skin. The View, acting as Observer [10], is then responsible for filling in the dynamic parts of the presentation into the static parts that are determined by Skin.
The Subform and Reusable Subform patterns [5] can be used in concert with Skin. Each subform has its associated skin, and the nesting of skins mirrors the subforms hierarchy. (See Implementation note ý5.)
We would like to thank our PLoP2000 shepherd Mark Bradac and our meta-shepherd Dwight Deugo for their helpful comments, and the writers' workshop participants for their valuable feedback.
We thank EM-TECH group for providing the environment where the TextTemplate class could be created, and for providing resources that helped in writing the pattern.
[1] DC Micro Development. html++ CGI Class Library Online Manual. DC Micro Development, 1998.
[2] L. Stein. CGI.pm - a Perl5 CGI Library. Cold Spring Harbor Laboratory, 2000.
[3] Allaire Corporation. Developing Web Applications with ColdFusion. Allaire Corporation, 1999.
[4] Microsoft. Active Server Pages Tutorial. Redmond, MA: Microsoft, 2000.
[5] M. Bradac and B. Fletcher: A Pattern Language for Developing Form Style Windows. In R.C. Martin, D. Riehle, and F. Buschmann (eds.), Pattern Languages of Program Design 3. Reading, MA: Addison-Wesley, 1998.
[6] MSDN Library. Microsoft Foundation Class Library. Redmond, MA: Microsoft, 1998.
[7] Apple Computer, Inc. Inside Macintosh: Macintosh Toolbox Essentials. Apple Developer Connection, 1996.
[8] MSDN Library. Platform SDK: Windows User Interface. Redmond, MA: Microsoft, 1998.
[9] M. Froumentin. Extensible Stylesheet Language (XSL). The World Wide Web Consortium, 2000.
[10] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.
[11] R. Pinchuk and Y. Sharon. The Phrasebook Pattern. PLoP 2000 Proceedings.
[12] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and M. Stal. Pattern Oriented Software Architecture. New York: John Wiley & Sons, 1996.