Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Awful Programming Advice (bramcohen.livejournal.com)
25 points by lackbeard on Sept 17, 2009 | hide | past | favorite | 12 comments


I think it's a bit skeevy to start an article with "a square is not a subclass of a rectangle is not a subclass of a polygon" and end with "and maybe that's why Tornado couldn't use Twisted Python".


I think his point was that having a simple and clean design would make the software easily adoptable.

What's wrong with that?


Because things should be as simple as they can be, and no simpler. He made a case for simplifying square -> rect -> poly. He made no such case for Twisted, but still tarred them with the same brush.


That's a very interesting thought and I have a gut feel he's onto something.

Last two times I used subclasses were in case of a C++ library that was designed very much like twisted (except it used threading a great deal more): there were events coming from epoll() and going to a pool of worker threads.

I subclassed this framework to create my own asynchronous network server. However, that got me thinking: wouldn't this have been better implemented using callbacks?

The other time I've used subclassing was in Java with the "decorator" pattern: I needed to extend the functionality of object foo, while still allowing any subclasses of foo to behave as they normally would. However, the Scala approach to this (mixing in traits) seems a great deal more elegant.


Classes (and OOP) are there to help you, and they apply to a specific set of problems - use them where they help, and don't when they don't.

I just finished a game with monsters, soldiers, snipers, movable barricades, etc. Someone who's class-crazy would probably make a soldier class that inherits from a generic "entity" class, ditto for each monster type, etc - I don't think that's efficient, I wind up with too many files and too much repetition. Instead I built a single "character" class with a constructor that takes a "type" - monster, soldier, etc - and drive behavior of each method with "if" statements where required.

I'm sure some purist is having heart strictures right now, but I'm not writing an API - I'm working in a living structure where the map format and drawing format may change from day to day, and I don't want to make updates in half a dozen files if the rules for aiming change. I want a single "aim" method that behaves differently based on type.

I subclass system objects all the time of course, but like the author I rarely subclass my own objects. Am I being pragmatic or foolish?


With respect to game entities, the industry flirted with inheritance hierarchies when they started using C++ in the mid-90s, but in this decade it's mostly shifted to a compositional approach where an entity is just a structure that ties together different components.

In my latest game project I made two major decisions about entities that really helped later:

-Representing entities as property lists + a unique id.

-Generating some of the entity logic based on state transition systems I make and export from QFSM.

The first part means that I can start with JSON as my map format and reuse them at runtime without a difficult "parse" step, by having the spawn code just dump more fields on top of the original object. There's some clunkiness in that I end up with a lot of potential for name collision, but so far it hasn't become something dangerously complex.

The second part is something I'm pretty proud of, as it came from the realization that the major problem with entity state is not the states themselves, but the transitions between states; those have to be kept well-formalized or you lose track very quickly. Implementation as an FSM is not exactly correct because an FSM navigates its way through the entire graph on every pass, while an entity fits better with a continuation-style approach, where on each frame it starts with a transition to use, does processing within that transition and logic to choose the next one, and then releases execution until the next frame. If I ever want to do multiple transitions in a single frame that's possible simply by calling transition() recursively.

Generating the code to choose a transition means that I barely have to think about that part: I draw up the graph, export it, and then add some lexical macros to my code to wire it up. I decided to use this system for the main loop as well as entities as it gave me more guarantees about how the code path progresses, the timing of different events, initialization/cleanup, etc., and the results were really good - the code factored itself quite naturally and I'm experiencing an absence of many types of bugs that normally haunted me in the past.

The next thing I want to try, which I'm not doing for this game, is to generate code for managing components as well; I have a lot of boilerplate for registering and unregistering the components at spawn/despawn right now, and the components aren't formalized very well; they tend to manipulate fields on the entity somewhat abusively. If I can clean that up, it'll probably be even easier to work with.


> but I'm not writing an API - I'm working in a living structure where the map format and drawing format may change from day to day, and I don't want to make updates in half a dozen files if the rules for aiming change.

I find that I am always writing APIs, and it's not always clear which is going to be 'public' or 'private'. That is, I generally work bottom-up, and my lower-level classes present a sensible API that I can work with directly, e.g. in irb. This makes the design of higher-level classes easier, if I've done the low-level API correctly.

> I want a single "aim" method that behaves differently based on type.

Hm, you mean like _char.aim_? Where char may be a Monster, Soldier, or ?


>I find that I am always writing APIs, and it's not always clear which is going to be 'public' or 'private'.

I understand the sentiment, and I believe in SOLID principles. I just mean that I'm not building a library that other people will use. No one is ever going to subclass my "player" class, not even me. Unlike my sound or graphics or AI code it's unlikely to be reused even in another game.

>Hm, you mean like _char.aim_? Where char may be a Monster, Soldier, or ?

Yes. My "aim" method is 90% identical for all types, about 10% specific to the type - if(type==monster){do this}; if(type==soldier){do that}

I've heard "if you're using if statements like this you're probably not using OOP correctly", but in my case I don't want six nearly identical methods spread out in six class files. That's repeating myself, which is the real sin I'd rather avoid; it comes back to haunt me if the map format or some other data structure changes.


> if Twisted followed this example straightforwardly .. then Tornado would never have been written.

The only reason Tornado came about is that it is far more interesting to write your own code than to learn someone else's. It has very little to do with whether Twisted was designed well or not.



I think he's right.

If you end up thinking about non-trivial tree of classes, like shape-circle-oval, square-rect-polygon, triangle - you're doing it wrong! I don't know why, but it just does never seem to work. You can generalize it to a polygon, tho, and it would work.

What you really want from subclassing is interfaces (Set and List come to mind immediately) and civilized monkey-patching (you have a class, you want some of its behavior changed, so you override a method or two).


Excellent point,

It's worth having Objects represent a particular hierarchy of characteristics that you would like to concentrate on and to allow other characteristics be represent by containment and list objects. I've programming in c++ w/ QT lately and it seems like it actually has most of what I've gotten used to in Ruby if I represent a significant amount of the data structures with hash and list templates rather than trying to represent everything with the class construct.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: