Large classes are a result of the "one more thing" problem. You have a perfect class that does one thing, and your users like it. However, they want it to do one other thing and another and then another. Before we realise it, the class becomes responsible for many different things.
All hope is not lost. If you can identify these different responsibilities, you can extract the properties and functions to a new class or move them to an existing class where they belong.
Eureka can give you a helping hand when it comes to solving this problem.
Where to start?
Every class has a vocabulary unique to the problem statement it is catering to. To surface this information, Eureka has the vocabulary panel. Once you have the tool up and running on the class you are interested in, click on the "Words" tab on the vocabulary panel.
From the list of words, see the ones with a smaller frequency count than those on the top. The count is a relative measure, so finding words with a "mid-range" frequency count could work best. I am interested in duration, volume, and chrome for this example.
Identifying a cluster
A cluster is a group of properties and functions that fulfil a responsibility. These members can either be transferred to a new class or moved to an existing one. For example, if the members contribute to the feature envy code smell, we move them to an existing class. We might create a new class if it is a missing abstraction problem. But before we address either one of these code smell, we need to identify the cluster. Eureka provides you with a tool that can help.
The cluster identification feature specifies a start node and one or more hub nodes. Using this information, Eureka can traverse the graph and identify subgraphs that fulfil a responsibility within the class.
Start node
The start node can be any node that matches terminology or search criteria. It does not have to be the "first" node in the graph.
Hub node
When you traverse the nodes in the graph, there could be nodes with unusual incoming or outgoing dependencies. If your start node is connected to one of these hub nodes, it could traverse the majority or almost the entire graph. To prevent this from happening, we need to specify these nodes to the tool so that we can stop traversing beyond these hub nodes.
Some candidates for hub nodes are,
- Entry points to the framework classes (e.g.)
onCreate(Bundle)
andonViewCreated(View, Bundle)
forActivity
andFragment
classes in Android. It could be controller functions for web frameworks. A class can have one or more entry-point functions. - Constructors,
<init>()
- Class initializer,
<clinit>()
- Shared dependencies (e.g.) several functions may use view model properties in an Android application.
In general, look for members with many incoming/outgoing dependencies.
Using the tool
Press "Cmd + K" or "Ctrl + K" to bring up the dialog to specify the start and hub nodes. As you add more and more hub nodes, you'll notice the network shrink smaller and smaller. Here are a few examples with their corresponding start and hub nodes.
Example 1: Duration
Example 2: Volume
Example 3: Chrome
Next steps
After identifying a network, figure out the kind of code smell and refactor away. Possible code smell can be,
- Missing abstractions (Missing class or Primitive obsession)
- Feature envy
If you don't have Eureka, install it using Homebrew.
brew install legacycodehq/tap/eureka
You can check out the project page, and if you like the tool, consider starring the project on GitHub! 🌟