Data Clumps

Description (What)

An identical group of variables show up repeatedly.

These could be variables for connecting to a database, a client or repeatedly being passed across methods

How to Locate It (Where)

Look for repeated sets of variables in method arguments

How It Manifests (Why)

It's often easier to keep sending the list of variables to different functions.

It could also be due to 'copypasta programming'.

How to Fix It (Possible Treatments)

In Example 1, Extract Class is used to create a new class for the data clumps and the new class is passed in method arguments instead of the variables separately.

We want to be able to reduce the amount of parameters we pass to methods and also by passing an object of a class created for parameters, we make it easier to change anything about the parameters themselves in a single place instead of in all methods that take those parameters.

In Example 2, the pair of start date and end date are appearing frequently inside the parameters, then we consider Introduce Parameter Object

to wrap it as a Object, reducing the number of parameters.

Other treatments are also possible based on the specific scenario, they can be found here

Examples

Example 1

Before:

The connection variables for the MongoDB connection are an identical group of variables that need to be grouped together.

Observed Code Smells:
- Data Clumps (lines 2-5) 

After:

Applied Extract Class on the variables to create their own class to be passed to the actual connecting class

Refactoring Applied:
- Data Clumps
    - Extract Class (MongoDbConnectionVariables)
Observed Code Smells After Refactoring:
- None

Example 2

Before:

There are several pair of start and end dates of type Date appearing in the parameters.

No matter it is a start date for amounts, checking for overlaps or leases, they are a pair of data which can be introduced

as a Paramter Object.

After:

Apply Introduce Parameter Object by creating a new class called DateRange,

encapsulating both start and end of type Date and check for overlaps by passing in another DataRange instance.

Refactoring Applied:
- Data Clumps:
    - Introduce Parameter Object (line 7-21, line 28, line 32, line 42, line 52, line 67)
Observed Code Smells After Refactoring:
- None

When to Ignore

In cases where passing an object in method parameters might create an undesirable dependency between two classes

More

More about Data Clumps

001  enum ConnectionState {
002   CONNECTED,
003   FAILURE,
004   NOTCONNECTED
005  }
006  
007  class MongoDBConnector {
008   private ConnectionState state;
009  
010   public MongoDBConnector() {
011   this.state = ConnectionState.NOTCONNECTED;
012   }
013  
014   public boolean isCredentialsValid(int port, String host, String username, String password) {
015   if (port < 0 || port > 65535) {
016   throw new IllegalArgumentException("Port is invalid");
017   }
018  
019   if (!host.startsWith("http")) {
020   throw new IllegalArgumentException("Host is invalid");
021   }
022  
023   return true;
024   }
025  
026   public boolean testConnection(int port, String host, String username, String password) {
027   if (Math.random() > 0.1) {
028   System.out.println("Connection to: " + host + ":" + port + "/" + username + "&" + password + "successful");
029   return true;
030   } else {
031   System.out.println("Connection to: " + host + ":" + port + "/" + username + "&" + password + "failed");
032   return false;
033   }
034   }
035  
036   public void connectToDatabase(int port, String host, String username, String password)
037   throws IllegalArgumentException {
038   if (!isCredentialsValid(port, host, username, password)) {
039   this.state = ConnectionState.FAILURE;
040   throw new IllegalArgumentException("Invalid database credentials");
041   }
042  
043   System.out.println("Testing connection...");
044  
045   if (!testConnection(port, host, username, password)) {
046   this.state = ConnectionState.FAILURE;
047   throw new IllegalArgumentException(
048   "Failed trying to connect to database: " + host + ":" + port + "/" + username + "&" + password);
049   }
050  
051   System.out
052   .println("Connection to database estabilished: " + host + ":" + port + "/" + username + "&" + password);
053   this.state = ConnectionState.CONNECTED;
054   }
055  
056   public String getCurrentState() {
057   return "State: " + this.state;
058   }
059  }
060  
061  public class DCBE1 {
062   public static void main(String[] args) {
063   MongoDBConnector mongodb = new MongoDBConnector();
064   mongodb.connectToDatabase(27017, "http://localhost", "yash", "fn023uc");
065   }
066  }
067