Amazon.com Widgets Even More Changes to HTMLWriter

Even More Changes to HTMLWriter

By Nick at January 09, 2011 14:19
Filed Under: Delphi, Software Development, Unit Testing

Okay, so if you are actually using THTMLWriter, I’m sorry – but I made another pretty major change to it, one that will require you to again make changes to any existing code that you have.  Such are the perils of using pre-1.0 software I suppose.

These changes are made at the suggestion of a number of you fine people, and I’m grateful for your wisdom and for taking the time to critique my work and make such good suggestions. 

Interface Only Access

If you have an interface you want people to use, then you should provide a way for them to use it.  In fact, the really cool thing to do is to make it so that it is the only way to do it.  One iron-clad rule of interfaces is that you should never mix interfaces and instances of the classes that implement them.  The interface is the important thing, not how the interface is implemented. 

An interface is a “promise” of a specific service, and a consumer of an interface shouldn’t care about the implementation details of an interface.  Indeed, that is one of the main advantages of interfaces – that you can “switch out” what you use to actually implement the interface.  If one of you guys things my implementation sucks and want to create a new one, you can simply create a class that implements IHTMLWriter and use that instead without changing any of the code that you have using IHTMLWriter.

So What Did I Do This Time?

Okay, so here’s what I did.

  • I created three standalone functions that return a reference to the IHTMLWriter interface.  They correspond to the three constructors that are included in the THTMLWriterclass.  This is the only way to “get at” the IHTMLWriter interface. 
  • I “hid” the entire THTMLWriterclass in the implementation section of the uHTMLWriter.pas unit.  Now, you can’t even get at the class even if you wanted to.  You must deal exclusively with the interface. 
  • I put the two interfaces I created – IHTMLWriter and ILoadSave -- in their own units.  This way, they aren’t coupled together and can be used separately if desired. 
  • I put all the /// comments in the interface unit instead of on the class. 
  • I had to make changes to the tests to support the new way of grabbing an interface reference.  I made the names of the functions similar enough to the code you current have that it will just be a matter of deleting a few characters.  Of course, all the tests still pass.

What’s Left?

I have a few more things to implement – mostly support for some less common tags that go in specific places inside a <table> tag.  After I do that, I think then I’ll declare THTMLWriter to be an official 1.0 release.

Comments (5) -

1/9/2011 3:54:08 PM #

John Jacobson

I agree completely regarding interfaces. Expose the interface publicly, along with a factory function, but keep the implementation private. In the class hierarchy I'm currently working on, only the interfaces are in the "interface" section of the unit, along with the signatures for their associated factory methods. The implementation is all hidden in the implementation section. This include the actual class declarations and definitions, even every type that doesn't need to be known outside the hierarchy. I find CodeExplorer from ModelMakerTools to be very valuable in this. I can create a working class, then abstract out the interface, make the class itself "private" (meaning inside the implementation section of the unit), all very quickly. In fact, by making the classes themselves descend from TInterfacedObject, I don't even have to worry about lifetime management, as long as I never use a class or class reference outside the unit. I think this is an excellent way to pursue true OO principles.

John Jacobson United States |

1/10/2011 1:01:42 AM #

Jolyon Smith

You don't need to break the instantiation syntax, just change the implementation detail of the syntax, i.e instead of stand-alone functions, why not class functions, invoking hidden (private/protected) constructors:

  class function THTMLWriter.Create(): IHTMLWriter;

as opposed to:

  constructor THTMLWriter.Create();


For sure this "exposes" an implementation detail of the specific implementation of IHTMLWriter, but this really is no different in the case of stand-alone functions... for practical purposes, there is nothing to say that the THTMLWriter class whose class function is yielding the interface is in fact that class that implements that interface.

The only other "exposure" involved is in having to locate the class in the interface section of the unit, but that's not actually relevant to the question of whether the implementation detail is "exposed" in terms of visibility in the language itself, just a semantic difference in the formatting of the file contents - people can physically view and read the implementation section just as easily as they can the interface section.

Jolyon Smith New Zealand |

1/10/2011 3:35:09 AM #

Iztok Kacin

@Jolyon

class functions would be one way to go, but I prefer another approach. When possible I hide the whole class implementation (interface derived classes that is) to the implemetation section and only leave interfaces in the interface section. This way I do not event allow the user to use the class by mistake. Most of the time only a function is needed to create the main (root) class of the whole framework. From then on everything is automanaged memory wise. This simplifies things a lot and leaves no room for user errors.

This is obviously not possible when you have multiple units that reference each other. Such as in cases, when you need to derieve classes from a base class and that base class is in its own unit. You cannot hide it in implementation section in such cases.

Iztok Kacin Slovenia |

1/10/2011 4:16:29 AM #

Jeroen Pluimers

I normally don't hide the implementing class (someone might want to derive from it in the future), but I do separate the interface factory in a separate unit.

So then you end up with at least 3 units (in this example for MyTool):

- MyToolInterfaceUnit
  Declares the IMyTool interface
- MyToolClassUnit
  Implements IMyToolInterface with the TMyCool class
- MyToolFactoryUnit
  Has functions that return a IMyTool interface reference implemented by a TMyTool class instance

--jeroen

Jeroen Pluimers Netherlands |

1/10/2011 11:29:35 AM #

nick

@Jolyon -

Thanks for the good idea -- I considered that approach but ultimately decided on the approach Iztok advocated.  The main reason I did that is because it leaves little doubt about how the interface is to be accessed, particularly for those who may still be somewhat unclear about interfaces and how they should be used.

However, I'm not totally wedded to it, and if it becomes clear that someone, as Jeroen mentioned, wanted to descend from it, I might consider moving it back out.

nick United States |

Pingbacks and trackbacks (1)+

Comments are closed

A Little About Me

Hey, I'm Nick.  I'm interested in Software Development, Leadership, and Basketball.  I'm a big fan of Delphi, but love all cool programming languages.

A Pithy Quote for You

"Life is pain, princess. Anyone who tells you otherwise is selling something."    –  Dread Pirate Roberts

Little Buttons and Stuff

Nick Hodges

Create Your Badge
View Nick Hodges's profile on LinkedIn
profile for Nick Hodges on Stack Exchange, a network of free, community-driven Q&A sites
Powered by DiscountASP.net
Join Dropbox

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.

Book Stuff

Earn Free Stuff

Search & Win