Special

Special Print Closeout!

We're clearing out the remainder of our print issues at fire sale prices -- as much as 75% off! Quantities are extremely limited and only available while supplies last. Hurry to take advantage of this one-time offer.

RBD Magazines

Once these printed back issues are gone, they are gone!

Article Preview


Buy Now

PDF:

Object-Oriented Thinking

Designing an HTTPResource Class

Procrastination-Driven Design

Issue: 6.6 (September/October 2008)
Author: Charles Yeomans
Article Description: No description available.
Article Length (in bytes): 5,254
Starting Page Number: 50
RBD Number: 6618
Resource File(s): None
Related Link(s): None
Known Limitations: None

Excerpt of article text...

In the course of my work, I wrote a web server. In this month's column, I talk about one piece of it, the representation of HTTP resources. To summarize the hypertext transfer protocol in a sentence, a web client sends a request to a server for a resource, and receives a response in return. In my server, I have corresponding classes HTTPRequest, HTTPResponse, and HTTPResource. The design and implementation of HTTPRequest and HTTPResponse are straightforward; one reads the HTTP specification and expresses it in REALbasic code. Implementing HTTPResource is another matter; after all, a resource can be just about anything. I knew what I didn't want to do -- an abstract superclass or class interface implemented in an ever-increasing pile of subclasses. Since I didn't quite know what I wanted to do, I used what we'll call procrastination-driven design. That is to say, I implemented the bare minimum needed to represent a resource, and put off the rest until later. Here is the result. Given a request, I have some other code that maps it to a resource (a dispatcher that uses regular expression matching on URLs). So, given a request and a resource, that resource should be able to respond to the request. Hence we have: Function AnswerRequest(request as HTTPRequest) As HTTPResponse The standard next step would be to write subclasses that implement the behavior of this function, either by overriding or via the implementation of an event handler defined in the HTTPResource class. Instead I opted to make the behavior configurable using delegates. Delegate Function HTTPRequestHandler(request as HTTPRequest) As HTTPResponse To HTTPResource I added computed properties for each of the HTTP verbs GET, POST, PUT, etc. The property corresponding to GET is GETHandler as HTTPRequestHandler; the naming convention should be obvious. Internally, I store these properties as values in a private Dictionary, with the keys being the HTTP verb names. Then the implementation of AnswerRequest is the following. Function AnswerRequest(request as HTTPRequest) As HTTPResponse if request is nil then return HTTPResponse.NewServerError end if dim handler as HTTPRequestHandler = me.HandlerMap.Lookup(request.Method, AddressOf RespondMethodNotAllowed) return handler.Invoke(request) End Function My HTTPRequest class has a function, Method() as String, that returns the HTTP verb of the request. Notice that this code supplies a default method handler. Private Function RespondMethodNotAllowed(request as HTTPRequest) As HTTPResponse return HTTPResponse.NewMethodNotAllowed End Function This means that I will only need to supply handlers as needed. The implementation of GETHandler (and the other handler properties) is extremely simple. Get return me.HandlerMap.Lookup("GET", nil) End Get Set me.HandlerMap.Value("GET") = value End Set Finally, HandlerMap is a private property of HTTPResource. Other than the remaining handler properties, that's the entire class. This design has proven to be quite flexible. For example, my URL dispatcher handles unmatched URLs by returning a so-called null object representing a missing resource. I build the object in a factory function. Private Function HTTPResourceMissingHandle(request as HTTPRequest) As HTTPResponse return HTTPResponse.NewNotFound End Function Function HTTPResourceMissing() As HTTPResource dim r as new HTTPResource r.GETHandler = AddressOf HTTPResourceMissingHandle r.POSTHandler = AddressOf HTTPResourceMissingHandle r.PUTHandler = AddressOf HTTPResourceMissingHandle r.HEADHandler = AddressOf HTTPResourceMissingHandle return r End Function An HTTPResource object representing an HTML file is built similarly. Private Function HTTPResourceFileHandleGET(request as HTTPRequest) As HTTPResponse //code that parses the URL and maps it to a file on disk dim response as HTTPResponse = HTTPResponse.NewOK //more code that reads the file contents and adds it to //an HTTPResponse object return response End Function Function HTTPResourceFile() As HTTPResource dim r as new HTTPResource r.GETHandler = AddressOf HTTPResourceFileHandleGET return r End Function I should like to point out that this code is not maintaining any program state, except of course the handler delegates. Everything else is computed as needed. This makes for easy debugging, it makes it easy to write a threaded version of my web server, and if performance issues arise, it should make it easy to cache objects where appropriate. Other resources may well require more complicated code to implement corresponding HTTPResource objects. But, so far, the design of HTTPResource keeps any such complication decoupled from HTTPResource. The addition of delegates to REALbasic makes it possible to implement classes in a more declarative style that results in far fewer subclasses. Now, subclassing can be reserved for those situations that really call for it; namely, when you need to extend the interface of a class. In your own code, look for subclasses that exist solely to implement behavior or supply data. You can replace them by a single, configurable class that will make your program design cleaner and more manageable.

...End of Excerpt. Please purchase the magazine to read the full article.

Article copyrighted by REALbasic Developer magazine. All rights reserved.


 


|

 


Weblog Commenting and Trackback by HaloScan.com