Philosophically, I agree. I view premature abstraction in the same light as premature optimization. I believe both abstraction and optimization are incarnations of your understanding of the problem. You want them in important places, not necessarily everywhere, and hence you want them late enough in the engineering process that you understand which places are important.
That said, within the comfort zone, there are high and low levels of abstraction. For example, in a project the size of Minecraft, I would expect CS major code to contain an ObjectFactoryFacadeCollection or two. Minecraft has nothing of the sort. It sticks almost exclusively to the Mob::HostileMob::Zombie inheritance we all grew up with. This is not bad or good[1], it's simply a reflection of the low-abstraction style of attacking problems that I associate with self-taught programmers.
On the other hand, its Magic Number to Constant ratio is downright scandalous . . . ;) Though it's possible that some of that is an artifact of the compilation/decompilation process.
Have you ever looked at a class named ObjectFactoryFacadeCollection and thought to yourself, "oh boy, this part will be fun to read?"
On one hand there's the complexity of the problem you're solving. On the other hand, there's incidental complexity. The ObjectFactoryFacadeCollection class squarely falls into the incidental complexity category. In other words, the moment you are writing a class of that sort, you have stopped working on solving the problem you set out to solve -- you're solving a problem that was invented by your tools, design, or limits of your understanding.
This isn't necessarily true. Sometimes you do in fact need these types of abstractions. This is why they've been made into patterns. The trick is to not use it before its necessary. The mere existence of it doesn't imply overengineered code.
Yes, of course you do sometimes need these types of abstractions, but you seem to have missed my point: they are a factor of incidental complexity. To restate, they are not at all inherent to the problem you are trying to solve. They are inherent to the tools with which you are solving the problem.
For instance, if your problem is calculating the trajectory of a projectile, a solution certainly exists that does not involve anything at all like an ObjectFactoryFacadeCollection. However, certain solutions involving unnecessarily complex abstractions could conceivably require one. This is incidental complexity. On the other hand, all solutions will require some information about the projectile's velocity, gravity, and so forth. This is complexity that is inherent to the problem itself.
Philosophically, I agree. I view premature abstraction in the same light as premature optimization.
It's usually easier to optimize later since optimisations are often just taking sections of code independantly and making them quicker. There is the whole 90/10 rule (or whatever it's called) that says it's better to highly optimise a few sections of bottleneck code rather than the whole thing.
Trying to retrofit an abstraction to a piece of code is almost always a horrible experience frought with mess and compromise.
Trying to retrofit an abstraction to a piece of code is almost always a horrible experience frought with mess and compromise.
Yes, and unless the problem is trivial or your experience in the domain is such that your foresight borders on the clairvoyant, this is guaranteed to happen. No matter how much (or little) design you do up front.
The key is to recognize the right time to stop and refactor, so as to keep the pain that comes with learning the problem space to a minimum.
Premature abstractions can have similar issues: unless you have more than 2 cases you don't necessarily know what your abstraction should look like. As the cases pile up you find yourself increasingly shoehorning implementations into abstractions that don't quite abstract correctly.
> Trying to retrofit an abstraction to a piece of code is almost always a horrible experience frought with mess and compromise.
It is amazing to me that our experiences are so different: I have found the exact opposite of this statement to be true. The only way that I've ever come up with a good abstraction is by starting with something concrete (preferably two or more instances) and factoring out the commonality. Retrofitting a piece of code to an abstraction that was designed in a vacuum tends to be an exercise in frustration, due to the abstraction being shortsighted and insufficiently suited to the problem space.
A well-abstracted program can be easier to optimize because modules are more loosely connected and their internal implementations can be replaced/optimized without breaking the rest of the code.
Philosophically, I agree. I view premature abstraction in the same light as premature optimization. I believe both abstraction and optimization are incarnations of your understanding of the problem. You want them in important places, not necessarily everywhere, and hence you want them late enough in the engineering process that you understand which places are important.
That said, within the comfort zone, there are high and low levels of abstraction. For example, in a project the size of Minecraft, I would expect CS major code to contain an ObjectFactoryFacadeCollection or two. Minecraft has nothing of the sort. It sticks almost exclusively to the Mob::HostileMob::Zombie inheritance we all grew up with. This is not bad or good[1], it's simply a reflection of the low-abstraction style of attacking problems that I associate with self-taught programmers.
On the other hand, its Magic Number to Constant ratio is downright scandalous . . . ;) Though it's possible that some of that is an artifact of the compilation/decompilation process.
[1] I'm lying, in this case it's a good thing.