Amazon.com Widgets Update on THTMLWriter

Update on THTMLWriter

By Nick at January 06, 2011 12:56
Filed Under: Software Development, Delphi, Tech Stuff, Unit Testing

I’ve made a fairly significant change to THTMLWriter.  (Even if you aren’t a use of THTMLWriter, please keep reading, there’s a valuable lesson at the end.)

What Did I Do?

I created an interface – IHTMLWriter – which simply lists the public interface of the class – and then had THTMLWriter implement that interface.  Not a big deal.  The big deal is that now the Fluent Interface is done with the IHTMLWriter interface instead of the class itself.  This shouldn’t be a big deal if you are using the class – you merely need to change the variable declaration to IHTMLWriter, and remove any code that destroys/frees the class you may have created. 

Why did I do this?  Well, I have to confess that it was partly because I could.  But I also think that in general, interfaces are better to deal with, particularly with the fluent interface.  I’ve noticed that interfaces appear to be the typical way that such things are implemented.  In addition, an interface limits the scope available to the consumer of a class.  If  you can only use a class via the interface, I as the developer can decouple the implementation from the interface – something that is always good. Plus, if one of you fine people feels like there is a better way to crank out HTML, now you can do it, too, using the same interface. 

The Valuable Lesson

Here’s the part that you all need to read:  I was able to make this rather large change with a lot of confidence because I had a pretty extensive set of unit tests to back me up. 

The initial change – creating the IHTMLWriter interface and changing the class to implement and use it – broke everything.  No test would pass, and the test program did nothing but raise access violations.  So, of course, I went to work and fixed things up.  I had to change a little architecture, alter the way a few things were done, and generally fix things up. 

All of my debugging was done on the test project.  It provided a perfect, simple reproduction of the bugs.  The unit tests enabled me to easily step through simple examples and find the places where the bugs were and where things weren’t working right.  And eventually the tests all “turned green”. 

GoingGreen

And let me tell you, turning green is sweet!  First, it feels really good. I kept running the tests and watching that little green bar slide across because it felt great.  Why did it feel great?  Because I knew that my changes were complete and  because I had confidence that things were all working as they were supposed to.   The only way that was possible was because of the existence of the unit tests.  Without them, I’d never really ever be sure that things were back to normal and working.

Time Well Spent

So the unit tests allowed me to forge ahead with confidence, provided me with a useful and powerful vehicle for testing and debugging my changes, ensured that I would know when I was done, and enabled me to prove that things were working as they should – all while making a fairly substantial change to a fairly complex code library. 

And some people say they don’t have the time to write unit tests.  I say you don’t have the time not to.

Comments (8) -

1/6/2011 2:25:43 PM #

codeelegance

Its a shame there are still so many that haven't discovered the value of unit testing.

Concerning your changes...

If you added a class function along the lines of:

class function THtmlWriter.Write: IHTMLWriter
begin
  Result := THtmlWriter.CreateDocument;
end;

Could you then use it without even needing to declare a variable to hold the htmlwriter:

THtmlWriter.Write
              .OpenHead
                .AddAttribute('dweezle')
                .AddText('farble')
              .CloseTag
              .OpenBody.AddAttribute('ding')
                  .OpenSpan
                    .AddAttribute('this', 'that')
                    .AddStyle('font: italic')
                    .OpenDiv
                      .AddAttribute('floo')
                      .AddText('Blah')
                    .CloseTag
                  .CloseTag
                  .AddText('Hoorah')
                  .AddBoldText(' Shadooby')
                  .OpenBold
                    .AddText('Goombah')
                  .CloseTag
              .CloseTag
          .AsHTML;

Just a thought.

codeelegance United States |

1/6/2011 3:12:35 PM #

nick

Great idea!  Consider it done.

nick United States |

1/6/2011 5:34:39 PM #

Chris

Insofar as you're absolutely in favour of interfaces, you could in fact go a small step further and, instead of exposing a Write class method, move the existing constructors back to protected visibility, give them a suffix (say 'Obj'), and add equivalent class methods at the public level instead, e.g.

class function THTMLWriter.Create(aTagName: string;
  aCloseTagType: TCloseTagType = ctNormal;
  aCanAddAttributes: TCanHaveAttributes =
  chaCanHaveAttributes): IHTMLWriter;
begin
  Result := CreateObj(aTagName, aCloseTagType,
    aCanAddAttributes);
end;

I'm not necessarily advocating such an approach mind, just highlighting its possibility.

Chris United Kingdom |

1/7/2011 9:10:03 AM #

nick

Chris --

That's another good idea -- I'll give that some thought.

nick United States |

1/7/2011 3:44:25 PM #

Eric

Ah, fluent style, or how to have a whole bunch of code packed in a single statement that support neither breakpoints nor debug stepping without the CPU view... and for which call stacks crash reports are of virtually no use.

Eric France |

1/8/2011 6:40:03 PM #

Lars Fosdal

I am still wondering how you would do conditional HTML code i.e. include/exclude different sections of HTML code, depending on a runtime condition.

Lars Fosdal Norway |

1/9/2011 1:28:36 PM #

Uwe Raabe

@Lars: The Fluent Interface is not able to work across conditional statements. In that case you have to store the current result in a variable and use that in the different conditional branches as a new root.

Uwe Raabe Germany |

Pingbacks and trackbacks (1)+

Comments are closed

My Book

A Pithy Quote for You

"When buying and selling are controlled by legislation, the first things to be bought and sold are legislators."    –  P. J. O'Rourke

Amazon Gift Cards

General Disclaimer

The views I express here are entirely my own and not necessarily those of any other rational person or organization.  However, I strongly recommend that you agree with pretty much everything I say because, well, I'm right.  Most of the time. Except when I'm not, in which case, you shouldn't agree with me.

Month List