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);
}
}
-
\$\begingroup\$ I think it would be useful to remove the multiple blank lines from the above \$\endgroup\$Brian Agnew– Brian Agnew2012年08月01日 13:00:11 +00:00Commented Aug 1, 2012 at 13:00
2 Answers 2
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.
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.