Simpler designs make the least assumptions and are therefore the most flexible. And here's the way to create designs that are as simple as possible:
- First, write all your code in a single method.
- Then, extract repeated code into submethods (your IDE probably has a shortcut for this). The scope of local variables is also a great guide on what code to extract into a submethod. The number of local variables in a method is a great estimate for its complexity and coherence. (For example, if one variable is only used at the top and bottom of a method, but not in the middle, then maybe some of the middle code should be extracted.)
- Once you have several methods which all use the same variables (either in their argument lists or by accessing a subset of the object's instance variables), then that's a good sign to extract all those methods into a new class.
- Finally, use interfaces to capture common behavior and use superclasses to extract shared instance variables from different subclasses. In most cases, this will satisfy all your abstraction needs! Abstract classes, super() calls, all those features and almost never needed!