1
\$\begingroup\$

I have two Java classes with very similar code. Basically they are almost the same except for a few method calls etc, i.e. replace ip with msisdn in the classes and they would be identical.

How can I refactor them to remove the duplication? Or maybe if there is a Design Pattern I should be using?

public class IpQuery {
 private final CommandLineInterface commandLineInterface;
 private final CSVFileReader csvFileReader;
 private final XMLFileReader xmlFileReader;
 private final CacheQueryRequester cacheQueryRequester;
 private final CacheQueryResponseParser cacheQueryResponseParser;
 /**
 * Invokes a cache query using the ip as a key into the session cache, displaying the result on the command line.
 * <p>
 * @param ipToQueryCacheFor the ip to query the PCC-A session cache for.
 * @param commandLineInterface object used to display error/warning/info messages on the command line. 
 */
 public IpQuery(String ipToQueryCacheFor, CommandLineInterface commandLineInterface) {
 this.commandLineInterface = commandLineInterface;
 this.csvFileReader = new CSVFileReader(commandLineInterface);
 this.xmlFileReader = new XMLFileReader(commandLineInterface);
 this.cacheQueryRequester = new CacheQueryRequester();
 this.cacheQueryResponseParser = new CacheQueryResponseParser();
 queryCacheForSessionWithIp(ipToQueryCacheFor);
 }
 private void queryCacheForSessionWithIp(String ipToQueryCacheFor) { 
 final String configFile = commandLineInterface.getConfigFileArgValue(); 
 if (configFile != null) {
 if (configFile.endsWith(".xml")) {
 final ArrayList<String> pccaPoolHosts = xmlFileReader.read(configFile);
 queryPrimaryAndSecondaryPCCAForIp(ipToQueryCacheFor, pccaPoolHosts);
 } else {
 final ArrayList<String> pccaPoolHosts = csvFileReader.read(configFile);
 queryPrimaryAndSecondaryPCCAForIp(ipToQueryCacheFor, pccaPoolHosts);
 }
 } else {
 commandLineInterface.displayMessage(
 "Warning: No CSV or XML config file entered, defaulting to querying localhost [::1].");
 invokeCacheQueryForIp(ipToQueryCacheFor, "::1");
 }
 }
 private void queryPrimaryAndSecondaryPCCAForIp(String ipToQueryCacheFor, ArrayList<String> pccaPoolHosts) {
 final PCCAServerSelector pccaServerSelector = new PCCAServerSelector(pccaPoolHosts, ipToQueryCacheFor);
 commandLineInterface.displayMessage(
 "Info: Querying primary PCC-A [" + pccaServerSelector.getPrimaryEndpoint().getIPAddress() +"]");
 invokeCacheQueryForIp(ipToQueryCacheFor, pccaServerSelector.getPrimaryEndpoint().getIPAddress());
 commandLineInterface.displayMessage(
 "Info: Querying secondary PCC-A [" + pccaServerSelector.getSecondaryEndpoint().getIPAddress() +"]");
 invokeCacheQueryForIp(ipToQueryCacheFor, pccaServerSelector.getSecondaryEndpoint().getIPAddress());
 }
 private void invokeCacheQueryForIp(String ipToQueryCacheFor, String pccaHostToQuery) {
 final PayLoad cacheQueryResponsePayLoad = cacheQueryRequester.queryCacheForSessionWithIp(ipToQueryCacheFor, pccaHostToQuery);
 final String cacheQueryResponseAsString = cacheQueryResponseParser.parse(cacheQueryResponsePayLoad);
 commandLineInterface.displayMessage(cacheQueryResponseAsString);
 }
}
public class MsisdnQuery {
 private final CommandLineInterface commandLineInterface;
 private final CSVFileReader csvFileReader;
 private final XMLFileReader xmlFileReader;
 private final CacheQueryRequester cacheQueryRequester;
 private final CacheQueryResponseParser cacheQueryResponseParser;
 /**
 * Invokes a cache query using the msisdn as a key into the session cache, displaying the result on the command line.
 * <p>
 * @param msisdnToQueryCacheFor the msisdn to query the PCC-A session cache for.
 * @param commandLineInterface object used to display error/warning/info messages on the command line. 
 */
 public MsisdnQuery(String msisdnToQueryCacheFor, CommandLineInterface commandLineInterface) {
 this.commandLineInterface = commandLineInterface;
 this.csvFileReader = new CSVFileReader(commandLineInterface);
 this.xmlFileReader = new XMLFileReader(commandLineInterface);
 this.cacheQueryRequester = new CacheQueryRequester();
 this.cacheQueryResponseParser = new CacheQueryResponseParser();
 queryCacheForSessionWithMsisdn(msisdnToQueryCacheFor);
 }
 private void queryCacheForSessionWithMsisdn(String msisdnToQueryCacheFor) { 
 final String configFile = commandLineInterface.getConfigFileArgValue(); 
 if (configFile != null) {
 if (configFile.endsWith(".xml")) {
 final ArrayList<String> pccaPoolHosts = xmlFileReader.read(configFile);
 queryPrimaryAndSecondaryPCCAForMsisdn(msisdnToQueryCacheFor, pccaPoolHosts);
 } else {
 final ArrayList<String> pccaPoolHosts = csvFileReader.read(configFile);
 queryPrimaryAndSecondaryPCCAForMsisdn(msisdnToQueryCacheFor, pccaPoolHosts);
 }
 } else {
 commandLineInterface.displayMessage(
 "Warning: No CSV or XML config file entered, defaulting to querying localhost [::1].");
 invokeCacheQueryForMsisdn(msisdnToQueryCacheFor, "::1");
 }
 }
 private void queryPrimaryAndSecondaryPCCAForMsisdn(String msisdnToQueryCacheFor, ArrayList<String> pccaPoolHosts) {
 final PCCAServerSelector pccaServerSelector = new PCCAServerSelector(pccaPoolHosts, msisdnToQueryCacheFor);
 commandLineInterface.displayMessage(
 "Info: Querying primary PCC-A [" + pccaServerSelector.getPrimaryEndpoint().getIPAddress() +"]");
 invokeCacheQueryForMsisdn(msisdnToQueryCacheFor, pccaServerSelector.getPrimaryEndpoint().getIPAddress());
 commandLineInterface.displayMessage(
 "Info: Querying secondary PCC-A [" + pccaServerSelector.getSecondaryEndpoint().getIPAddress() +"]");
 invokeCacheQueryForMsisdn(msisdnToQueryCacheFor, pccaServerSelector.getSecondaryEndpoint().getIPAddress());
 }
 private void invokeCacheQueryForMsisdn(String msisdnToQueryCacheFor, String pccaHostToQuery) {
 final PayLoad cacheQueryResponsePayLoad 
 = cacheQueryRequester.queryCacheForSessionWithMsisdn(msisdnToQueryCacheFor, pccaHostToQuery);
 final String cacheQueryResponseAsString = cacheQueryResponseParser.parse(cacheQueryResponsePayLoad);
 commandLineInterface.displayMessage(cacheQueryResponseAsString);
 }
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Aug 1, 2012 at 9:44
\$\endgroup\$
1
  • \$\begingroup\$ I think it would be useful to remove the multiple blank lines from the above \$\endgroup\$ Commented Aug 1, 2012 at 13:00

2 Answers 2

3
\$\begingroup\$

If I'm right the only difference is this line:

final PayLoad cacheQueryResponsePayLoad 
 = cacheQueryRequester.queryCacheForSessionWithMsisdn(msisdnToQueryCacheFor, 
 pccaHostToQuery);

ant the CacheQueryRequester class could be similar to the following:

public class CacheQueryRequester {
 public PayLoad queryCacheForSessionWithMsisdn(final String msisdnToQueryCacheFor, 
 final String pccaHostToQuery) {
 ...
 }
 public PayLoad queryCacheForSessionWithIp(final String ipToQueryCacheFor, 
 final String pccaHostToQuery) {
 ...
 }
}

To remove the duplication I'd change the CacheQueryRequester to an interface with only one method:

public interface CacheQueryRequester {
 public PayLoad queryCacheForSession(final String queryCacheFor, 
 final String pccaHostToQuery);
}

And create two implementations: IpCacheQueryRequester and MsisdnCacheQueryRequester. Finally, rename one of the original query classes simply to Query and modify its constructor to the following:

public Query(final String queryCacheFor, final CommandLineInterface commandLineInterface, 
 final CacheQueryRequester cacheQueryRequester) {
 ...
 this.cacheQueryRequester = cacheQueryRequester;
 ...
}

Now, you can pass an IpCacheQueryRequester or an MsisdnCacheQueryRequester instance to the constructor and get rid of the other query class.

answered Aug 1, 2012 at 12:43
\$\endgroup\$
2
\$\begingroup\$

From memory I think Eclipse does an "Extract Supertype" which might help you pull out a base class. Using inheritance may not be the best way of solving this but it will at least give you the common code.

answered Aug 1, 2012 at 15:24
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.