One of the surest ways to create designs that aren’t easily reused is to try to anticipate the domain-specific data structures that will be needed and create them in advance. Like so much design advice this requires some caution to apply.
The domain-specific part is crucial. General purpose data structures are very reusable. One general purpose data structure is a list. A domain specific data structure is a list of Employees. How do you know when a strongly typed list of Employees is going to be reusable? One clue is that you yourself find that you need it. Another clue is that you keep running across code where devs create collections (or arrays) of Employees and manipulate them in some way. If you don’t find yourself in need of a data structure and you can’t find evidence of other devs needing/using that data structure then it is probably too early to create that data structure.
Since most of us are terrible at predicting the future the odds of getting something like a domain-specific data structure right are low. Since we’re more likely than not to get it wrong our prematurely created data structure might get in the way of discovering the right data structure.
I find myself most able to re-use components that are highly cohesive and loosely coupled. High cohesion means that a given method does only a single thing at the level of abstraction appropriate to its context. Loose coupling reduces the number of dependencies between a given method or object and other methods or objects.
In practice, loose coupling is mainly about choosing what to parameterize. For any non-trivial method it’s usually too expensive to parameterize every thing. If a method can usefully perform its highly cohesive functionality with only its input parameters then that method is loosely coupled. These are great candidates for public entry points because they don’t make much in the way of assumptions about conditions. Every method doesn’t need to be extremely loosely coupled but those that are tend to be more easily reused then those that aren’t. Of course this has to be weighed against readability – a method that takes 500 parameters might be extremely reusable but it won’t be reused much because it’s too much work to use.
For example, a method that sorts a list of strings tends to be highly reusable when working with lists of strings. A method that takes a list of strings, tokenizes them, executes them in a shell, collects the output and summarizes the execution results will tend to be less reusable except in a very specific context.
The second method is less reusable because it does so many things. I’m less likely to be able to compose functionality from that method because it does things I don’t want it to do. The fewer things a function does the less likely it is to do something I don’t want and, consequently, the more likely it is to be reused.