• Refactoring to Patterns suggests that using patterns to improve an existing design is better than using patterns early in a new design.*


This is pretty much what I'm up to at the moment in SdiDesk. For the new release I'm putting a lot more of the objects behind interfaces.

For example, I've started pulling apart the monolithic BigBallOfMud that is the Model****Level object. The idea of this object was to provide a single point of contact to the model (int the MVC sense) part of the program. Behind sits all the functionality of the model including the store for pages, the transformation from wiki-markup to the final HTML pages for native or export, the crawlers, the link-type management, the time-index stuff, the engine for "preparing" pages by executing macros etc.

It turns out that many of these things have needed to talk back to the model. As a simple example, on first sight it seems the Wiki****To****Html converter should be able to operate independently of the model. Just give it a string containing wiki-words and get it translated to HTML.

Unfortunately, the appearance of links in HTML can differ depending on whether the page exists or not. So that information needs to be passed to the renderer.

One possibility is that the renderer takes a boolean representing whether the page-exists. But who's responsibility is it to find this out? And what if a new kind of renderer requires other contextual information? For this reason I decided instead to included a reference to the model itself in the call to the renderer. That way, the renderer can always go back to the model to get further information it might need to turn the link into HTML (eg. the colour according to it's type)

But this is bloody nuisance when you try to write test programs. Ideally, I want to create a test program just for the renderer. But if I need to include a Model****Level as an argument for the render function, the test program will also need to know about them. And in order to know about those, it needs to know about all the other component objects that make up the model.

As the model gets more complex and contains more moving parts, it becomes more and more of a pain to include these other objects in the test-program simply to make a model which can be passed as a parameter. [The http://www.refactoring.com/catalog/extractInterface.html The solution is clear]. I need to make an interface which represents only that part of the model which really is of interest to the renderer, and pass that. With that I can create a dummy or stub object that implements the same interface with suitable responses for the questions the test-program asks.

But what are the parts of the model of interest to the renderer? What part should be carved out as this interface? Before writing the first version of SdiDesk, I had no real clue. Now, with a working program, it starts to become more clear where the boundary should be drawn. As I add new tests for the functionality of the renderers, the parts of the model that are needed to help address them become glaringly obvious.

The redesign of the program is iterative, evolving the continuous negotiation between four elements : the test-programs, the renderers, the interface, and the Model****Level class. My test-program are not yet UnitTests, they're too informal, relying me looking at the output to judge if they pass. But they are starting to drive the changes, I make them ask the questions of the renderer first. Then I work on the renderer, as and when I discover I need new information about the model, I move to adding an appropriate way of getting this information to the interface. That in turn forces me to adapt the model (which implements this interface, to providing it, and to adapt the stub to provide suitable dummy responses.)

Interfaces are good to carve-up large objects this way. They separate the users of a complex object from the object itself. Allows them to be more easily tested. And ultimately allows the object to be broken into sensible sub-modules, based on how the other parts of the program really use it.

Backlinks (1 items)