Rendering Notes into Text
I'm a bit torn by a code architecture problem. A "Note" is a point in 2D tonal space, defined by the number of octaves and fifths that it is away from the origin note [0, 0] (which is also known as Re0). On the one hand, an abstract data type such as Note should not know anything about how it is rendered into graphic images or into text. The note [-2, 4] can be rendered in text as
Likewise, the interval [-2, 4] can be rendered as
The simple/stupid thing to do is have a method in the Note class, called something like toString(), that renders 'this' note into a canonical text form (e.g., "[-2, 4]"). Because strings are such a basic data type, having the Note class "know about" (and hence depend on) the String class is perhaps not a terrible violation of encapsulation.
But what about the rendering "Mi0"? To produce this, the Note class would have to know about (and hence depend on) the classes of Flex's Text Layout Framework, which is a pretty clear violation of Note's encapsulation.
Likewise, an Interval can be rendered into at least three different text strings ("[-2, 4]", "major third", "M3"). Should the Interval class implement methods for all of these different renderings? What if I want to add a new rendering? After doing so, I'd have to recompile all of the classes that used the Note class. Currently, my code base is small enough that this would be no big deal, but once I've got dozens of lessons, this kind of change could lead to a testing nightmare.
It seems to me that I've encountered a classic example of where the Factory design pattern is appropriate. I think I need a separate entity -- something that is not a Note, but which knows about Notes -- which renders Notes into...somethings.
There's be an abstract RenderNote class, with a method that would, given a Note, return an Object containing its rendering. RenderNote would have an abstract RenderNoteToString subclass, which would return a String for a given note. RenderNoteToString would have the subclass RenderNoteToStringVector, which would return the string "[-2, 4]" for the example note.
Then, RenderNote would also have a RenderNoteToTextFlow subclass, which would return a TextFlow (e.g., "Fi0") for a given note.
There would be a similar RenderInterval class hierarchy.
This all seems very complicated (as Factory deisgns usually are), but it would cleanly separate the text-rendering function out of Notes and Interval into separate classes. If, later on, I needed to render Note objects into a new kind of text representation, I could just add a new subclass to the RenderNote hierarchy; no recompilation or testing of the existing codebase would be necessary.
The result would be very much like Flex 4's use of "skins" for the graphical rendering of a UI control. The "RenderX" class could be seen as a "text skin," so to speak.
But...do I really want to mess with all of that complexity now? Or should I just slam a toTLFName() method into the Note class, and refactor it into a Factory if and only if it becomes a problem?
I think I'll do the latter.
- the string "[-2, 4]"
- the Text Layout Framework "TextFlow" data structure that represents "Fi0"
- ...and so on.
Likewise, the interval [-2, 4] can be rendered as
- the string "[-2, 4]"
- the string "major third"
- the string "M3"
- an arrow pointing from a given note to another note two notes rightward
- ...and so on.
The simple/stupid thing to do is have a method in the Note class, called something like toString(), that renders 'this' note into a canonical text form (e.g., "[-2, 4]"). Because strings are such a basic data type, having the Note class "know about" (and hence depend on) the String class is perhaps not a terrible violation of encapsulation.
But what about the rendering "Mi0"? To produce this, the Note class would have to know about (and hence depend on) the classes of Flex's Text Layout Framework, which is a pretty clear violation of Note's encapsulation.
Likewise, an Interval can be rendered into at least three different text strings ("[-2, 4]", "major third", "M3"). Should the Interval class implement methods for all of these different renderings? What if I want to add a new rendering? After doing so, I'd have to recompile all of the classes that used the Note class. Currently, my code base is small enough that this would be no big deal, but once I've got dozens of lessons, this kind of change could lead to a testing nightmare.
It seems to me that I've encountered a classic example of where the Factory design pattern is appropriate. I think I need a separate entity -- something that is not a Note, but which knows about Notes -- which renders Notes into...somethings.
There's be an abstract RenderNote class, with a method that would, given a Note, return an Object containing its rendering. RenderNote would have an abstract RenderNoteToString subclass, which would return a String for a given note. RenderNoteToString would have the subclass RenderNoteToStringVector, which would return the string "[-2, 4]" for the example note.
Then, RenderNote would also have a RenderNoteToTextFlow subclass, which would return a TextFlow (e.g., "Fi0") for a given note.
There would be a similar RenderInterval class hierarchy.
This all seems very complicated (as Factory deisgns usually are), but it would cleanly separate the text-rendering function out of Notes and Interval into separate classes. If, later on, I needed to render Note objects into a new kind of text representation, I could just add a new subclass to the RenderNote hierarchy; no recompilation or testing of the existing codebase would be necessary.
The result would be very much like Flex 4's use of "skins" for the graphical rendering of a UI control. The "RenderX" class could be seen as a "text skin," so to speak.
But...do I really want to mess with all of that complexity now? Or should I just slam a toTLFName() method into the Note class, and refactor it into a Factory if and only if it becomes a problem?
I think I'll do the latter.
Labels: programming


0 Comments:
Post a Comment
Links to this post:
Create a Link
<< Home