TL;DR Software Engineers see world in patterns, they look for common denominators, not accepting the fact that something may be an one & only instance. That's why we sometimes end up building frameworks for single problem occurrences ("we'll gonna need it in future") or try to squish accidentally similar concepts into one, common form - ignoring the fact that Domain experts don't recognize this similarity (it has no advantage in terms of domain, it doesn't constrain real-world domain development). The proper answer is (of course) to fight off instinct of pre-mature generalization & ... duplicate.

It always starts with two.
Two of Something. Cases. Actions. Items. Entities. States. Whatever.
It takes only a fraction of second for an eye of a skillful engineer to detect common parts: behaviour / properties / whatever.

AHA!
Inheritance!
Polymorphism!
Ge-ne-ra-li-zation!
Let's build a piece of framework around it so AAAAALL the future cases will immediately snap into where they belong!

... which they never do.

Let's face the fact: we, as engineers, tend to look for (familiar) patterns wherever we turn our attention to. And we dare to call it "design" or (worse) "modeling". What should be (but rarely is) alarming is the fact that it's us (engineers) who identify these constructs (like inheritance) in the Domain, not the users or Domain experts. In our hubris, we are proud of that & shake our heads with pity:

"How is it even possible that they didn't notice that? Fortunately they have US! We came & scratched down the foundation of a framework in 5 minutes!"

<sarcasm>Because life is so freaking simple & everything is just an exemplification of Gang-of-Four patterns, right?</sarcasm>


I think that your attitude towards generalisation is just another maturity indicator for engineer - to determine whether you are able to fight an innate urge to pre-maturely generalize, you should answer yourself few questions:

  1. Do you wait until you have at least 3 instances of "something" to draw first conclusions whether there's actually a real regularity in-game?
  2. Do you confirm these findings with the actual perception of various actors who deal with this "something" in real Domain?
  3. Do you understand the difference between domain modelling & forceful application of object-oriented design?

Issue is much more important than it may seem at the first glance. Generalisation due to accidental similarity (not in Domain's "nature") has quite a few negative effects, but first of all:

Solution is very brittle as there's absolutely NO GUARANTEE that future cases will conform to generalised model - you know the rest of the story: change have to be implemented anyway, engineers dive even deeper into further generalisation of stuff that has fewer and fewer similarities ... Cost of change (even simple one) skyrockets, so does frustration of developers ...

But shouldn't we pro-actively look for patterns, unification & general code re-use? Otherwise we may end up with duplicating code which is supposed to be one of software engineering deadliest sins (as in DRY), right?

Nope, not right. There's one important misconception here:

Duplicating code is ... OK! As long as a change to the system won't cause all of copies to be "touched" (modified). If changes are local to particular copy (instance), duplication is more than justified - you just let the variants diverge in directions determined by continuously emerging requirements.

It may be less appealing for your engineering ambitions, but ... this is how real world works - concepts like inheritance or polymorphism look great in Animal + Cat + Dog tutorial, but reality is far more complex (even its simplified version we're supposed to model while building software).

Pic: © 1999 Astralwerks. All rights reserved.