Wednesday, April 6, 2011

Programming to interfaces and abstract classes

So while working on the (relatively) small chemical structure drawing project I've been trying to incorporate maintainability into the design. After all, you never know when you'll be reusing the code you've written in the future (I never thought I'd be using the rendering code from nearly a year ago, and it looks terrible!)

One of the ways I'm trying to achieve this is by maintaining a strict separation of graphics-based code in the CompoundViewer from the more behind-the-scenes stuff. This is slightly problematic because by nature, the CompoundViewer serves as the graphical front-end to the graph representing the molecule. In my chemical database project, the CompoundViewer is tightly coupled to using GDI as its graphical front-end, resulting in Graphics objects sprinkled throughout the code.

Well, as much as I like GDI, it's not infeasible to want to use a different rendering library (for example, Direct X for a 3D version)

To keep the CompoundViewer reusable, I've created an abstract CompoundViewer class which implements the generic code that doesn't depend on the graphics library (which is mostly all of it).

Then, for a specific library there is a concrete class (such as GDICompoundViewer) which has any specific code in it. The CompoundViewer then provides a constructor which takes in an IRenderer object (an interface so we can stub it easily in testing).

Some Renderer implementations might need special initialization through their constructor, such as the GDIRenderer which requires a Graphics object. For this case, there is an abstract RendererFactory class which provides an IRenderer object in its createRenderer() method. The GDIRendererFactory takes in a Graphics object in the constructor, and creates a new GDIRenderer object with that Graphics object and returns it in the createRenderer() method.

With this architecture, most of the CompoundViewer code is in the abstract class since it is generic, and there is very little in the concrete graphics-based classes. The benefit of using interfaces allows us to stub out dependency on a graphical front-end while testing. Imagine setting up Direct X in your unit tests. What a mess!

No comments:

Post a Comment