The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Descriptor Method Pattern

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
Descriptor Method Pattern Posted: Dec 12, 2004 8:24 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: Descriptor Method Pattern
Feed Title: David Buck - Blog
Feed URL: http://www.cincomsmalltalk.com/rssBlog/buck-rss.xml
Feed Description: Smalltalk can do that
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From David Buck - Blog

Advertisement
There's an object oriented pattern I find useful from time to time and because of its novel approach, I thought I'd write about it.

Suppose you need to read and write a fixed column file format. Your read code might look something like this:

MyFileFormat

readFrom: aStream
   self
      recordNumber: (aStream next: 10);
      recordType: (aStream next: 15) ;
      systemName: (aStream next: 5);
      isActive: (aStream next: 1)

I'll leave the write code as an exercise for the reader.

There are a few problems with this. First, there are no data conversions being done. All the fields are being read as strings when certain fields should be converted to Integers, Booleans and Symbols.

Another problem, though, is that the file format is coded into the read method as well as the write method. This means that there are two different methods which must remain in sync. This violates DRY (Don't Repeat Yourself) and causes maintenance problems.

One way to tackle the problem is to build an object structure to describe the file format and get that structure to read and write the files.

MyFileFormat

format
   ^FileFormat new
      addField: #recordNumber width: 10 type: #integer;
      addField: #recordType width: 15 type: #symbol;
      addField: #systemName width: 5 type: #string;
      addField: #isActive width: 1 type: #boolean;
      yourself.

readFrom: aStream
   ^self format readFrom: aStream

For simple formats, this will work easily. If, however, the file format varies depending on the values of some fields, it gets more complicated. If, for example, the recordType is "AddNetworkElement", then the format of the rest of the line has information on the network element to add (possibly an IP address and a name). To have these conditions checked by an object structure, you would have to implement an Interpreter pattern which will really complicate the design.

The solution I use is one I call a "Descriptor Method". You write a method that describes the format but delegate all messages to a helper object stored in an instance variable. Here's what it looks like:

MyFileFormat

format
   self
      field: #recordNumber width: 10 type: #integer;
      field: #recordType width: 15 type: #symbol;
      field: #systemName width: 5 type: #string;
      field: #isActive width: 1 type: boolean;
      when: #recordType is: #AddNetworkElement do: [:helper2 |
         helper2
            field: #ipAddress width: 15 type: #string;
            field: #name width: 10 type: #string]

field: aSymbol width: anInteger type: typeSymbol
   ^helper field: aSymbol width: anInteger type: typeSymbol

when: aSymbol is: anObject value do: aBlock
   ^helper when: aSymbol is: anObject do: aBlock

If the helper instance variable can contains a FileReader (which we'd have to write), then running the format method causes us to read a file using that format.

A FileReader could look like this:

FileReader

field: aSymbol width: aNumber type: typeSymbol
   dictionary at: aSymbol put: (self readFieldWidth: aNumber type: typeSymbol)

readFieldWidth: aNumber type: typeSymbol
   | valueString |
   valueString := stream next: aNumber.
   typeSymbol = #integer ifTrue: [^Integer readFrom: valueString readStream].
   typeSymbol = #symbol ifTrue: [^valueString asSymbol].
   typeSymbol = #boolean ifTrue: [^valueString = 'T'].
   self error: 'Unknown type symbol ', typeSymbol

when: aSymbol is: value do: aBlock
   (dictionary at: aSymbol) = value ifTrue: [aBlock value] 

If the helper variable contains a FileWriter, then the format method causes us to write out a file using that format. We can also write helpers that report the file format without reading or writing anything. Another kind of helper could use code generation to create a DTO (Data Transfer Object) with the appropriate instance variables and accessor methods required by the format. Another kind of helper could verify that a file conforms to the format without reading its contents.

This technique can be used in more cases than just reading fixed file formats. It can be used to create SUnit testcases which include descriptions for a UI as well as the actual test code.

testSetAdd
   self
      | set |
      name: 'Test Set Add';
      description: 'Test the addition of elements to a set';
      test: [
         set := Set new.
         set add: 3; add: 4; add: 7; add: 3.
         self assert: set size = 3]

A GUI can plug in a TestCaseInformationHelper, run the method and collect the name and description of the test without running the test code. To run, plug in a TestCaseRunnerHelper. It will ignore the name and description and run the test code.

This technique works best in a language with lexical closures like Smalltalk. With lexical closures, the helper objects can choose whether or not to run the blocks. Without it, the descriptor methods have to ask the helpers whether or not to run the conditional code.

Read: Descriptor Method Pattern

Topic: Information Smog Previous Topic   Next Topic Topic: Subsystem Exception Handling in Smalltalk

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use