Problem

You’ve written an object or class. It’s somewhat complex and you want to iterate ways to better abstract your code but are unsure how to start. There exists documentation about what good abstraction looks like, but no concrete iterative steps.

Being deep inside your own code can make you blind to obvious alternatives.

Solution

Create a bipartite graph of instance methods and member variables. Draw edges between methods and the variables they use. Then move isolated components into separate classes.

Concrete, Simple Example

A space ship

Assume you’re designing a video game with a space ship. Space ships are circles that have some amount of health they can also heal (if they aren’t dead already). Space ships also have some location and you need to know if two space ships intersect.

First version of our ship abstraction

Let’s created a bipartite graph of instance methods and member variables to see if we can simplify this abstraction.

Bipartite graph of methods and members

Notice how we have two disconnected subgraphs. One connecting health with heal or harm, and another connecting intersects with location and size. This is a sign we are probably dealing with two separate game concepts: one about killing actors of the game and another about objects with physical locations. We can use composition to break our ship object into two concepts.

Abstracted ship

Notice how Ship2 still does the same amount of things, but we can now reason about those things as smaller units. As a bonus, we have discovered the concepts of physical objects and killable objects. You could imagine making smaller interfaces or packages around these atomic units, spreading this abstraction to other places of your game.

Complex objects are difficult to reason about. Smaller objects are easier to fit into our mental space.

Conclusion

It’s not necessary to literally draw out a bipartite graph to simplify software, but knowing the process is something you can often do in your head to create smaller abstractions.