Tuesday, January 11, 2011

OOP and prototyping

This post should delight my OOP-hating friends. But hey, I'm not here to defend OOP at all costs, but rather to talk about my experiences with it, and how to make the best of it.

One thing that I found OOP is good at is imposing "design up front" approaches. OOP is all about putting structure on your code. Unfortunately in early stages / prototyping, you will not necessarily know what kind of structure your code should have just yet. Unless you have a ton of experience or a crystal ball (preferably the latter), you will likely take wrong turns in the design. Granted, those wrong turns are temporary, and can be corrected by future refactoring, but refactoring is not free. It costs time and effort. In the meantime, there is an additional cost and effort to working with OOP code that is structured inconveniently. If you got the structure wrong, and your data members belong to the wrong class, you need to create accessors and forwarding calls in order to access data that's nested. It's not uncommon to see something like:
 GetDataManager()->GetChineseGrammar()->ExtractCharacter1FromContext(GetContextManager()->GetContext(x))
in bad OOP code, because it's trying to access data that has been hidden in the wrong class.

Due to lack of foresight / experience, or even simply due to the nature of user requirements, I found myself temporarily locking myself down to bad partial designs. This then resulted in a struggle between growing functionality on top of a temporarily bad design versus improving the design now in hopes of quicker development later. As those familiar with my blog would easily guess, I opted for the "improve the design now" option. The problem is, however, "improve the design" meant moving from one structural lock-down to another. Development went in stages of progressively better design, each stage dictated by the results of the previous one. In the early iterations this was not particularly convenient, since requirements were "soft" and so was my understanding of where this project should go.

I always did find that this process was more and more satisfying as the project reached its final iterations, because the structural lockdown (i.e. what classes have access to what data members), needed to change less and less. OOP's biggest benefit may just be at the end of the development cycle, when all the pieces are in place and most use cases have been fit into the framework. Bug fixing on a well-written OO program is easy, because the final design reflects the use cases and reasoning about the use cases should be similar to reasoning about the program itself.

Anyways, back to the prototyping stage. I found that one of the best ways to keep the structure "soft" and maleable is to start by using structs (or classes with all public members) and global functions. Then slowly, as use cases become clear, those functions would move into the structs, initially as public functions, more data members will become private, and some of the functions also, and eventually we end up with our standard OOP, with data hiding, and all that good stuff.