Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think this is still very much applicable in OOP.

Developers tend to break complex business logic within classes down into smaller private methods to keep the code DRY. The “push ifs up” mantra is really useful here to ensure the branching doesn’t end up distributed amongst those methods.

The “push fors down” is also very relevant when most call threads end up at an expensive database query. It’s common to see upstream for loops that end up making many DB calls downstream somewhere when the looping could have been replaced by a “where” clause or “join” in the SQL.

In fact, the “push fors down” mantra is useful even at the architecture level, as it’s usually better to push looping logic for doing aggregations or filtering into a DAO where it can be optimized close to the DB, rather than pulling a bunch of objects out of the database and looping through them.

I love simple and clear design principles like this!

Though, as with all design principles, one needs to consider it deliberately vs applying it as dogma!



I've been experimenting with a similar idea to "pushing ifs up" in OOP, too: Question ifs in the code of a class and just use more classes.

For example - python terminology, as it came from there - don't have a class "BackupStatus", and have the BackupStatus use 2-3 Booleans to figure out how to compute a duration, a status message and such. Instead, have a protocol "BackupStatus" to declare what a BackupStatus has to do, and figure these out for a FailedBackup, a PartialBackup, a SuccessfulBackup and so on.

It's different from what I did for a long time, and some people are weirded out by it at first. But at the same time, it allows me to create a bunch of small, complete and "obviously correct" classes. Or at the least, very concrete cases to reason about with somewhat technical stake holders. And once your backup status classes / enum members / whatever are just correct, then you can go and discuss when to create and use which of states.

It's not for everything naturally, but it quickly gives a solid foundation to build more complex things upon.


It does enable proxy shenanigans though, which is something to be mindful of:

I can be reasonably certain of what a reasonably complicated piece of logic is doing/capable of doing.

Comprehending a function using an object implementing a protocol, on the other hand, will end up requiring me to cross reference logic across 4 or more files before I can feel confident knowing what to expect: the protocol definition and/or the basic implementation, the calling context, the factory context that was responsible for creating the object, and the actual definition of the current object. All of which may be trivial, but you need to actually look at them all.

It's the difference between a breaker box and a house of light switches: the switches are all trivial (you hope), but you still hit the breaker when you need to be sure that you are going to be surprised.


Yeah. I'm kinda thinking about writing a blog post about that project, because I kinda like how it turns out.

Imo, the method I described to me is mostly reasonable to implement closed and fairly complete domain objects. In this case, understanding the protocol is more akin to understanding the concept from the problem domain, and the implementing classes are different cases or states the thing can be in. In your image, I'd compare the main thing I use this for as fuses. Simple, encapsulated and fulfilling a purpose - just implemented in different ways depending on the context.

On the other hand, if you're dealing with implementing a flow chart, I've grown to very much like a decently structured imperative function - supported by these smaller objects.




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

Search: