Patterns in Web-based Systems

Patterns in Web-based Systems

Using design patterns with NeXT's WebObjects framework

by Don Yacktman and Alex Duong Nghiem

Published in Object Magazine, July, 1996, 6(4), pp. 56-58.



By encapsulating ideas and design decisions, design patterns efficiently describe how complex objected oriented (O-O) systems work. With a thorough understanding of just a handful of these patterns, we can approach a complex framework and quickly comprehend its structure. Web based O-O systems are no different. In this article we apply patterns from Design Patterns 1 to explore WebObjects, a sophisticated framework from NeXT Software Inc. for delivering Web-based applications2.

Before diving into the design patterns in WebObjects, it is important to understand the product itself. NeXT's WebObjects is a framework for building object-oriented applications on the web. Unlike Java applets, WebObjects works on the server side, creating dynamic web pages. The pages come from HTML templates, but the content is inserted dynamically by WebObjects. The content can come from data files, a DBMS, an ORB, or other data sources.

When using WebObjects, an object is created to represent each page in the application. These objects are also called components, in WebObjects' parlance. The components are defined in a rather unusual way, because there are three different files required by WebObjects. The first file is an HTML template that specifies a component's HTML representation. A new tag, <WEBOBJECT>, is used to insert dynamic content into the template. The second file is called a script file and contains the component's implementation--the code that defines the behavior of the component's methods. The third file contains "declarations" that provide a mapping between the HTML template, WebObjects classes, and variables in the component's script.

Although this arrangement may seem cumbersome at first, it greatly adds to WebObjects' flexibility. In the file system, each component is represented by a directory with the three files in it. For example, a component named "Main" would be a directory called "Main.wo" with the three files "Main.wos", "Main.wod", and "Main.html" in it. See figure 1 for a diagram of this arrangment.


Figure 1. File system representation of a component called "Main".

The scripts that define a WebObject are written in a new language called "WebScript," which is an interpreted object oriented language--a variant of Objective-C. It looks very much like SmallTalk with C control structures. The only type is "id," a pointer to an object, and messages look like SmallTalk blocks. For example:

[receiver message];
[receiver message: argument];
[receiver message: [receiver2 message2]];
[[receiver message1] message2: argument1 with: argument2];

Some more details of WebScript will be discussed below, but there isn't much more to it. Anyone with prior object oriented experience will find that WebScript is simple enough to easily learn in an hour or two.

Hello World

With this foundation as a starting point, let's look at the WebObjects version of "Hello World" The application consists of two pages. The first page is an HTML form which allows a name to be entered. Submit the form and the second page is returned, which greets the user and inserts the name from the form into the greeting. This application requires two components. The first page that WebObjects displays is always the component named Main. The greeting is on the other page, so the component for that page is called Hello.

The first component, Main, would have an HTML template, (Main.html), that looks like this:

<HTML> <HEAD> <TITLE>Hello World!</TITLE> </HEAD>
<BODY>
   <FORM>
	What is your name?
	<P>
	<WEBOBJECT NAME = "NAME_FIELD"><INPUT TYPE = "TEXT"></WEBOBJECT>
	<WEBOBJECT NAME = "SUBMIT_BUTTON"><INPUT TYPE = "SUBMIT"></WEBOBJECT>
	</P>
    </FORM>
</BODY> </HTML>

This HTML form has two WebObjects components in it--a text field for entering a name and a button to submit the form. So that WebObjects knows how to create the requested components, we have to tell it what classes should be used. This is done in the declarations file. Here is Main.wod:

NAME_FIELD: WOTextField {value = nameString};
SUBMIT_BUTTON: WOSubmitButton {action = sayHello};

The first thing on the line is the "name" parameter from the <WEBOBJECT> tag. This is followed by a colon and then the class name of the component that should generate the HTML which replaces the <WEBOBJECT> in the template. In this example, the NAME_FIELD is a WOTextField, one of several built-in components. The SUBMIT_BUTTON is a WOSubmitButton, another built-in component. There are built in components for every type of HTML form element. Figure 2 shows how the above HTML template would render in a browser, given the above declarations.


Figure 2. "Main" component of Hello World application.

In the declarations, the parts in curly braces allow certain parameters of the component to be connected to variables and methods in the script. The parameters change, depending upon the component used. For example, the NAME_FIELD's value, which corresponds to whatever the user types into the text field, will be stored into a variable called "nameString" that is declared in the script.

The SUBMIT_BUTTON's parameter is a little different: it specifies the name of a method within the script that should be called when the submit button is clicked. This is an example of the application of a design pattern called "Command." The command design pattern, in short, encapsulates a user request. In this case, the request occurs when the submit button is clicked, which submits the form. By using the declarations file to specify what action will be taken, the WOSubmitButton has been decoupled from its target so that the action taken can be changed by the developer. Because of the dynamic nature of WebScript, the implementation of the Command pattern is simplified, as shown in Figure 3.


Figure 3. Simplified Command design pattern.

In this case, the declarations file specifies the "sayHello" method. When WebObjects gets the form back, the method "sayHello" will therefore be invoked. Let's look at the script to see what will happen:

id nameString;

- sayHello
{
	id nextPage;
	nextPage = [WOApp pageWithName:@"Hello"];  
	[nextPage setTheName:nameString];
 	return nextPage;
}

There are two things in the script. First is the declaration of the "nameString" variable, which stores the input taken from the NAME_FIELD component, as described previously.

The second is the declaration of the method "sayHello". The method first declares a temporary variable called "nextPage". That page is initialized by calling something called "WOApp" to get the component "Hello". WOApp is an application object controlling the event loop for a WebObjects application. It also handles global tasks such as locating components within the local file system. In this case, it is locating the second page of the application.

Next, a message is sent to the page with the greeting. The message sets an instance variable called "theName" to the value stored in the "nameString" variable. We then return the page. This will cause the WOApp to return the new page to the user's browser as a result of their submitting the form.

To see what is actually returned, we need to look at the Hello component. The HTML template is even simpler than the template for the Main component:

<HTML> <HEAD> <TITLE>Hello World!</TITLE> </HEAD>
<BODY>
    Hello <WEBOBJECT NAME = "NAME_STRING"></WEBOBJECT>!
</BODY> </HTML>

There is only one component here, named "NAME_STRING" and there are no forms. Let's look at the declaration file to see what component is used:

NAME_STRING: WOString { value = theName };

NAME_STRING is simply a string object, and its value is taken from the script's "theName" instance variable. Since there are no actual instance methods needed, the script simply needs to declare the variable:

id theName;

The astute reader will have asked about the "setTheName" method that was invoked by the Main component. It turns out that all methods of the form "setFoo" and "foo" where "foo" is the name of an instance variable are generated automatically by WebObjects. As a result, the script for the Hello component needs no code. Assuming I entered the name "Don" in the form on the Main page, the Hello page will contain the text "Hello Don!".

The preceeding example has another design pattern hidden within it. As it turns out, the application object WOApp is an example of the Singleton pattern. A singleton is a class which has one and only one instance. Under WebObjects, we access the single, shared instance of WOApplication by using "WOApp" which is actually shorthand for the message [WOApplication sharedInstance]. WOApplication is a class, not an instance. The sharedInstance method creates a WOApplication object if none exists or returns the singleton if it has been created.

So far, this seems like a lot of work--the above application could be put together much easier in perl. But the power of WebObjects comes in its ability to reuse components. The most striking example of this would be through its use of the Composite design pattern. Composite allows objects to be made up of other objects. The classic example is a vector graphics application that has a group feature. Several graphics primitives can be grouped together into a single object that responds as if it were a new primitive. It is composed of the individual lines, squares, etc.

A WebObjects component can be made up of several other components, too. We've already seen this with built-in components such as WOString, WOTextField, and WOSubmitButton, which were used to compose the components in the example above. However, user created components may also be used to create new components.

A good example showing the range of the Composite design pattern is to create a component, which is a reuseable footer element that can be placed onto every page in an application. We could start with this HTML template:

<HR>
<CENTER>
<h5>Questions?  Contact our
<a href="http://www.misckit.com/don/index.html">webmaster</a>
via email to
<a href="mailto:don@misckit.com"><em>don@misckit.com</em></a>.
</CENTER>

Since this doesn't contain any <WEBOBJECT> tags, there is no need for a script or declarations file. It should be noted that the HTML template above is not a template for a complete document. It is simply a small snippet of HTML that can be inserted into other pages. If the above component were called Footer.wo (with Footer.htmlas above) then it could be used in all the components that represent full HTML pages by inserting the following into the HTML template:

<WEBOBJECT NAME="FOOTER"></WEBOBJECT>

And the following would be needed in the declarations file:

FOOTER: Footer {};

What has been accomplished? We have saved some typing but, more importantly, we have also made a dramatic improvement in our site's future maintenance.

If every page contains the HTML and declaration above, then we can change the footer on every page of our site by simply editing one file--the HTML template in Footer.wo. As more and more components are designed, there is less and less work to do when creating a new page. Since each component is treated in an abstract fashion by WebObjects, it is up to a developer's creativity to put this power into use.

There are several example components which could be useful in getting a new web site started, such as calendars and hyperlinked outline widgets, available on NeXT's web site. Those using WebObjects for a while already have a good collection of reuseable components. This demonstrates how the Composite design pattern helps to give WebObjects much of its power.

Another pattern that is used in WebObjects is "Flyweight." A Flyweight is a small, primitive object that is shared in order to conserve resources. It differs from a Singleton in that there are typically several instances of the same class. They are all created once in their different possible flavors, and then shared from that point onward. One of the internal classes available to script writers is the constant string class "NSString". Instances of NSString are never actually copied; they are reference counted and reused extensively. Many WebObjects components initialize Flyweights in the awake method, an initialization method that is always called when a component is created, if the method exists. While not all uses of NSString follow the Flyweight pattern, many do.

Because WebObjects allow for a wide range of components, and include components that represent Java applets, Netscape plug-ins, and more, it is also within reason to speculate that the Builder design pattern is used internally to generate components. With the Builder pattern, the representation and construction of a complex object are separated. In WebObjects, this is partially exposed since the declarations file tells the construction process the details of creating various components, each of which is a representation. The same construction process is always used, but the results will vary. The Builder pattern is likely used within WebObjects to gracefully handle the the wide variety of data formats found on the web as well.

Although this introduction to WebObjects is brief, it is easy to see how design patterns come into play. Anyone interested in WebObjects should look for more information on NeXT Software, Inc.'s web site, http://www.next.com/. The complete documentation for WebObjects and a free version may be downloaded from NeXT's site. When looking at the WebObjects documentation, one can find many other design patterns used in WebObjects.

Because WebObjects was built with the same FoundationKit that underlies NeXT's OPENSTEP environment, there is a rich set of examples showing how to apply design patterns. Examples of Observer, Iterator, Memento, and Chain of Responsibility are just a few. Finding and recognizing these patterns will make it easier to grasp the inner workings of this complex kit of objects.

References

1E Gamma et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1994.

2NeXT Software, Inc. WebObjects documentation. http://www.next.com/Pubs/Documents/WebObjects/WebObjectsTOC.html




Alex Duong Nghiem is the President of Global Objects Inc., in Atlanta, GA.
Donald A. Yacktman is an independent consultant in Provo, UT.


This copy of the article has been slightly modified by Don Yacktman from the published version to improve readability. The most recent changes were made on July 8, 1996.






Contact information:

Donald A. Yacktman
4279 N. Ivy Lane
Provo, UT, 84604
Phone: (801)221-0344 (voice and fax)
Fax: call above number to have the FAX switched on


E Mail: don@misckit.com
Home page URL: http://www.misckit.com/don/index.html


Internet Link Exchange
Member of the Internet Link Exchange