It's me again..
As some of you may remember, I posted a series of notes about evolving an OO design a while back. Well, one of the late replies was so interesting that I thought it was worth starting a new thread for my response.
The short summary is that Aristotle followed my logic about halfway, then applied a couple ideas of his own, and posted a completely different result for comparison and contrast.
For one thing, I think it's incredibly cool that he chose to devote head-time to the idea. It's always fun to have someone demonstrate that they understand what you're saying by handing it back to you in a shape you didn't expect. (One might even consider it worthwhile to upvote his node -- hint, hint ;-)
On a less personal level, his post is a great follow-up to the intent of the original message.. namely, to show new OO programmers how objects end up looking the way they do.
You should probably read both nodes if you want to grok the following discussion in fullness, but the general idea is that I built a state machine using distinct objects to represent each state, and he built a state machine as a single object that contains a lookup table for all the states.
And to cut to the chase: his version works, too. That's what makes it such a great follow-up.
My design is decentralized, and his is centralized. Both options work, and each has its good points and bad points. It's impossible to declare one approach better than another on its own merits, and for this particular problem, the choice is pretty much a matter of personal taste.
I happen to like decentralized systems, because they force me to think about loose coupling.
<digression>
Since this is a semi-tutorial, the term coupling more or less means "How much other code do I have to read before I can understand this piece right here?" Tight coupling twists code from all over the program into a single, migrane-inducing snarl. Loose coupling means you can pretty much ignore everything but the piece you're looking at right now. Global variables tend to create tight coupling. So does looking into a data structure and twiddling its values directly. Function calls and data formatting protocols, OTOH, tend to promote loose coupling.
For OO programmers, things like encapsulation (the Has-A relationship) and public data attributes tend to create tight coupling, while 'protocol classes' (base classes that define a hierarchy's entire interface) and standalone classes promote loose coupling.
Another way of looking at the problem is to ask, "how many 'use' statements will I have to execute before this code runs, if all the classes live in separate files?" And "how many files will I have to read before I know the actual count?"
</digression>
Submerging one object in another usually makes it easier to see the high-level logic of a given operation, but it also increases the coupling between classes. The outer object can end up wanting all sorts of information from the inner object, until it becomes easier to demote the inner object to an ordinary data structure.
Sometimes that's useful, but sometimes it just buries the messiness one layer deeper in the design. As a general rule, I try to make my designs as decentralized as possible, and only embed objects when the alternatives are just too baroque to be reasonable.
For this design, I chose to make the states objects in their own right because i plan to make them pretty complex later on. The state machine will be nondeterministic, meaning that zero, one, or many transitions might be valid for any given input character. That makes finding the 'right' transition more complicated than it would be for a deterministic state machine, where you get exactly one transition (or an error) for every input character.
Had I been building a determinstic state machine, it's very possible that I would have gone with a centralized design. The transition table would be easy to work with, and there wouldn't be much point in promoting the states to objects in their own right.
As it stands, the process of finding the 'right' transition for a nondeterministic state machine (called subset construction) is complicated enough that it makes sense to let the individual states do some of the work.
Those requirements go beyond what I talked about in my original post, though. Nor am I absolutely positive that using a set of independent states is the best way of solving the problem. A single, centralized state machine may end up being a better design. The whole point of this exercise is to find out.
So kudos to brother Aritotle for coming up with a really good alternate solution based on the information he was given. The one tiny quibble I have is that his final version of pseudocode puts the 'rewind and restart' operations before the 'token' operation, and if you think about the actual implementation, it's easier to do them the other way around (use the last-start and last-accept pointers to find the string, then reset them)
Discussions like this are why I like this place so much. ;-)
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Evolution, v2.0
by princepawn (Parson) on Dec 03, 2002 at 14:22 UTC | |
by mstone (Deacon) on Dec 04, 2002 at 02:13 UTC | |
|
Loose coupling good...
by pdcawley (Hermit) on Dec 04, 2002 at 11:07 UTC | |
by Aristotle (Chancellor) on Dec 08, 2002 at 13:09 UTC |