The code below compiles and does its job, however lets say I needed to added another 100 if statements that uses a reference. Whats the most efficient way to write multiple if statements?
public String getForceDetails(String ref) {
if (ref.equals("IW1")) {
setupForces();
return (ForceDetails.get(0).toString());
} else if (ref.equals("SS2")) {
setupForces();
return (ForceDetails.get(1).toString());
} else if (ref.equals("WB3")) {
setupForces();
return (ForceDetails.get(2).toString());
} else if (ref.equals("IW4")) {
setupForces();
return (ForceDetails.get(3).toString());
} else if (ref.equals("WB5")) {
setupForces();
return (ForceDetails.get(4).toString());
} else if (ref.equals("SS6")) {
setupForces();
return (ForceDetails.get(5).toString());
} else if (ref.equals("SS7")) {
setupForces();
return (ForceDetails.get(6).toString());
} else if (ref.equals("WB9")) {
setupForces();
return (ForceDetails.get(7).toString());
} else if (ref.equals("IW10")) {
setupForces();
return (ForceDetails.get(8).toString());
} else {
return "\nNo such force";
}
}
private void setupForces()
{
ForceDetails.add(new starShip("IW1","Twisters",200,200,ForceState.DOCKED,10,0,0,false));
ForceDetails.add(new starShip("SS2","Enterprise",300,200,ForceState.DOCKED,0,10,20,false));
ForceDetails.add(new starShip("WB3","Droop",300,100,ForceState.DOCKED,0,0,0,false));
ForceDetails.add(new starShip("IW4","Wingers",200,400,ForceState.DOCKED,20,0,0,false));
ForceDetails.add(new starShip("WB5","Hang",400,300,ForceState.DOCKED,0,0,0,true));
ForceDetails.add(new starShip("SS6","Voyager",450,200,ForceState.DOCKED,0,15,10,false));
ForceDetails.add(new starShip("SS7","Explorer",120, 65,ForceState.DOCKED,0,4,5,false));
ForceDetails.add(new starShip("WB9","Hover",300,400,ForceState.DOCKED,0,0,0,false));
ForceDetails.add(new starShip("IW10","Flyers",200,100,ForceState.DOCKED,5,0,0,false));
}
The obvious choice would be a switch statement over ref:
switch (ref) {
case "IW1":
setupForces();
return (ForceDetails.get(0).toString());
break;
case "SS2":
setupForces();
return (ForceDetails.get(1).toString());
break;
// etc.
}
If the code to execute always looks the like this (calling setupForces() and getting the n-th element of ForceDetails) you could also use a map that lets you retrieve n (Map<String, Integer>) which you populate with key-value-pairs like "IW1"->0 and "SS2"->1 etc.
The map solution also has the charme that you do not need to repeat the code that is basically equal for all cases which would be rather inconvenient if you have to change this later.
Well there is no inbuilt solution to that. What you can do is create a Map of condition -> result and return the value to the key.
Map<String, String> mapCondToRes = new HashMap<>();
public String getForceDetails(String ref) {
setupForces();
return mapCondToRes.get(ref);
}
You can check for validations and put more conditions very easily and elegantly.
In the existing code, each invocation of getForceDetails with a valid ref causes adding of 9 entries to a collection of starships ForceDetails. It is doubtful that this behaviour is intended, possibly a lazy initialization of ForceDetails was implied.
Next, to get rid of the multiple if statements using of the map is definitely more preferable than converting into switch statement and copying the same multiple calls to setupForces(). It could make sense to create a map Map<String, starship> instead of the list and populate it in setupForces (so there's no need to have a separate map of references to the index in the list):
Map<String, starShip> forces = new HashMap<>();
private void setupForces() {
forces.put("IW1", new starShip("IW1","Twisters",200,200,ForceState.DOCKED,10,0,0,false));
forces.put("SS2", new starShip("SS2","Enterprise",300,200,ForceState.DOCKED,0,10,20,false));
// ... add other starships mapped by their ids
}
public String getForceDetails(String ref) {
if (forces.isEmpty()) {
setupForces();
}
return Optional.ofNullable(forces.get(ref))
.map(starShip::toString)
.orElse("No such force found");
}
Use a java stream, you should read about them, you'll find it useful later as you progress on Java.
For the following code, I assume you have a getter for your "key" values inside your starShip class ("IW1", "SS2", "WB3"...).
This code also assumes that you cannot change your current List approach, If you can, a Map would be a even better.
private static final String NO_FORCE = "\nNo such force";
public String getForceDetails(String ref) {
String result = ForceDetails.stream() // <-- It's even faster if you use `parallelStream` however is known to have non-thread-safe issues
.filter(starShipItem -> Objects.equals(starShipItem.getKey(), ref))
.map(String::valueOf).findFirst()
.orElse(NO_FORCE)
;
if (NO_FORCE.equals(result)) {
return NO_FORCE;
}
setupForces();
return result;
}
Also, I recommend you to take a look at hackerrank challenges, you'll learn a lot more there.
Related
I understand you can't return from a ifPresent() so this example does not work:
public boolean checkSomethingIfPresent() {
mightReturnAString().ifPresent((item) -> {
if (item.equals("something")) {
// Do some other stuff like use "something" in API calls
return true; // Does not compile
}
});
return false;
}
Where mightReturnAString() could return a valid string or an empty optional. What I have done that works is:
public boolean checkSomethingIsPresent() {
Optional<String> result = mightReturnAString();
if (result.isPresent()) {
String item = result.get();
if (item.equals("something") {
// Do some other stuff like use "something" in API calls
return true;
}
}
return false;
}
which is longer and does not feel much different to just checking for nulls in the first place. I feel like there must be a more succinct way using Optional.
I think all you're looking for is simply filter and check for the presence then:
return result.filter(a -> a.equals("something")).isPresent();
How about mapping to a boolean?
public boolean checkSomethingIfPresent() {
return mightReturnAString().map(item -> {
if (item.equals("something")) {
// Do some other stuff like use "something" in API calls
return true; // Does not compile
}
return false; // or null
}).orElse(false);
}
While #nullpointer and #Ravindra showed how to merge the Optional with another condition, you'll have to do a bit more to be able to call APIs and do other stuff as you asked in the question. The following looks quite readable and concise in my opinion:
private static boolean checkSomethingIfPresent() {
Optional<String> str = mightReturnAString();
if (str.filter(s -> s.equals("something")).isPresent()) {
//call APIs here using str.get()
return true;
}
return false;
}
A better design would be to chain methods:
private static void checkSomethingIfPresent() {
mightReturnFilteredString().ifPresent(s -> {
//call APIs here
});
}
private static Optional<String> mightReturnFilteredString() {
return mightReturnAString().filter(s -> s.equals("something"));
}
private static Optional<String> mightReturnAString() {
return Optional.of("something");
}
The ideal solution is “command-query separation”: Make one method (command) for doing something with the string if it is present. And another method (query) to tell you whether it was there.
However, we don’t live an ideal world, and perfect solutions are never possible. If in your situation you cannot separate command and query, my taste is for the idea already presented by shmosel: map to a boolean. As a detail I would use filter rather than the inner if statement:
public boolean checkSomethingIfPresent() {
return mightReturnAString().filter(item -> item.equals("something"))
.map(item -> {
// Do some other stuff like use "something" in API calls
return true; // (compiles)
})
.orElse(false);
}
What I don’t like about it is that the call chain has a side effect, which is not normally expected except from ifPresent and ifPresentOrElse (and orElseThrow, of course).
If we insist on using ifPresent to make the side effect clearer, that is possible:
AtomicBoolean result = new AtomicBoolean(false);
mightReturnAString().filter(item -> item.equals("something"))
.ifPresent(item -> {
// Do some other stuff like use "something" in API calls
result.set(true);
});
return result.get();
I use AtomicBoolean as a container for the result since we would not be allowed to assign to a primitive boolean from within the lambda. We don’t need its atomicity, but it doesn’t harm either.
Link: Command–query separation on Wikipedia
By the way if you really want to get value from Optional, use:
Optional<User> user = service.getCurrentUset();
return user.map(User::getId);
I have multiple conditions to check as shown below,
if(pouch.getStatus().equals("Finalized") || pouch.getStatus().equals("Ready")
|| pouch.getStatus().equals("Checkout") || pouch.getStatus().equals("Confirmed")
|| pouch.getStatus().equals("Book") || pouch.getStatus().equals("Started")
|| pouch.getStatus().equals("Inital") || pouch.getStatus().equals("Close")) {
// Body Implementation
}
Is there any easy way to check above conditions similar like SQL INcondition, so that code look simpler?
Let's take a look about SQL in features
SQL WHERE IN returns values that match values in a list
So I would use a collection, which implements from Collection<E> and had contains method, make the if statement simpler.
contains(Object o) Returns true if this set contains the specified element.
contains effect is very similar to SQL in.
1.add your multiple conditions in the collection, which implements from Collection<E>
Set<String> dict = new HashSet<String>();
dict.add("Finalized");
dict.add("Ready");
dict.add("Checkout");
dict.add("Confirmed");
dict.add("Book");
dict.add("Started");
dict.add("Inital");
dict.add("Close");
2.using contains to check input value whether exist in the collection.
if (dict.contains(pouch.getStatus()))
{
// do your logic
}
You can use the method matches which is available in String class,
if(pouch.getStatus().matches("Finalized|Ready|Checkout|Confirmed|Book|Started|Inital|Close")){
//your implementation goes here
}
List<String> listOfInputs = new ArrayList<String>();
// add elements in listOfInputs...
boolean isAvailable = listOfInputs.contains(pouch.getStatus());
SQL IN might return more than one result, but in your question, if one condition is satisfied the operation will terminate and return.
You can create an enum to hold all your conditions as shown below.
Assuming your Pouch class is this.
public class Pouch {
private final String status;
public Pouch(final String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
Here is your enum with the pouch status.
public enum PouchEnum {
Finalized, Ready, Checkout, Confirmed, Book, Started, Inital, Close
}
and check your condition as shown below.
if (PouchEnum.valueOf(pouch.getStatus()) != null) {
// Do some stuff
}
To make it cleaner you can use EnumUtils from apache commons-lang3 this make your code checking more cleaner as shown below.
if (EnumUtils.isValidEnum(PouchEnum.class, pouch.getStatus())) {
// Do some stuff
}
I hope this will help your code to be cleaner.
You can create custom function:
static boolean inCondition(String var, String... ins) {
for (String in : ins) {
if (in.equals(var)) return true;
}
return false;
}
and then use it in this way:
public static void main(String[] args) {
String pouch = "Ready";
if (inCondition(pouch, "Finalized", "Ready", "Checkout" ... )) {
// do something
}
}
Below snippet might help you.
String status = "COMPLETED";
List<String> statusList = new ArrayList<>(Arrays.asList("COMPLETED","INPROGRESS"));
if(statusList.contains(status)){
// do your stuff
}
Using Arrays.asList and then use contains might be the best way at least on my case.
if(Arrays.asList("Finalized", "Ready", "Checkout", "Confirmed",
"Book", "Started", "Inital", "Close").contains(pouch.getStatus())) {
// Body
}
I think if you use the "switch" conditional, the code reads better:
switch (pouch.getStatus()) {
case "Finalized":
case "Ready":
case "Checkout":
case "Confirmed":
case "Book":
case "Started":
case "Inital":
case "Close":
// your code
break;
}
For this particular scenario, I think it's a good candidate for a simple enum like this:
public enum PouchStatus {
FINALIZED, READY, CHECKOUT, CONFIRMED, BOOK, STARTED, INITIAL, CLOSE
}
Usage:
if(PouchStatus.valueOf(pouch.getStatus().toUpperCase()) != null) {
}
You can also move this string sanitizing logic inside a static method in the enum, which would look like this:
public enum PouchStatus {
FINALIZED, READY, CHECKOUT, CONFIRMED, BOOK, STARTED, INITIAL, CLOSE
public static PouchStatus fromDescription(String desc) {
return Arrays.stream(PouchStatus.values()).filter(e -> e.name().equalsIgnoreCase(desc)).findFirst().orElse(null);
}
}
Usage:
if (PouchStatus.fromDescription(pouch.getStatus()) != null) {
}
As a final note, if the Pouch object comes from ORM (e.g.: hibernate/jpa) you can just map these values to the according enum elements right in the entity mapping (pouc.getStatus() would already return a PouchStatus object instead of a String).
Here is full example
public class InConditionJava {
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arr = { "Finalized", "Ready", "Checkout" };
checkData(arr);
}
private static void checkData(String[] arr) {
Set<String> names = new HashSet<String>(Arrays.asList(arr));
System.out.println("AS Checkout is there in our arr is should return True>>" + names.contains("Checkout")); // true
System.out.println(names.contains("Book")); // false
}
}
Here is another way of initializing List in one line with all statuses, and then checking if the list contains the given status.
// Java 9 way of initializing List with one line
List<String> statuses = List.of("Finalized", "Ready", "Checkout", "Confirmed",
"Book", "Started", "Inital", "Close");
if (statuses.contains(pouch.getStatus())) {
// Body
}
You can create an array of all status, then check if pouch.getStatus() in in that list or not?
public String[] statusArray = new String[]{ "Finalized", "Ready","Checkout","Confirmed", "Book", "Started", "Inital", "Close"};
if( Arrays.asList(statusArray).contains(pouch.getStatus())){
//do something
}
There are already plenty of options here, but you could also use Stream for this task, if the version of the JDK you are using is >= 8:
String status = pouch.getStatus();
if (Stream.of(
"Finalized",
"Ready",
"Checkout",
"Confirmed",
"Book",
"Started",
"Inital",
"Close")
.anyMatch(status::equals)) {
// Body
}
The downside of this method, compared to Collection#contains, is that you must make sure that pouch.getStatus() is not null, otherwise you will get a NullPointerException.
I have the following class.
public enum EnumService
{
ONE, TWO, THREE, FOUR,.............HUNDRED;
//Values till HUNDRED
public static EnumService returnMockService(String request)
{
//some string match
if(request.matches("/abc*")){
return ONE;
}
//some other string is match
else if(request.matches("/bcd*"))
return TWO;
else if(request.matches("/decf*"))
return THREE;
//many else if conditions
else if(request.matches("/wxyz*"))
return HUNDRED;
return null;
}
}
The code is not standard with more if else statements.
I want to minimize the number of if calls in above method yet maintaining the return type as EnumService
Any better option to do this.
It would be great if some can help me in making it clean.
First: no need to else if you return.
Second: you can optimize it a LOT if you use this string as a parameter in the enum:
public enum EnumService
{
ONE("abc*"),
// etc
private static final Map<Pattern, EnumService> MAPPING
= new HashMap<>();
static {
for (final EnumService svc: values())
MAPPING.put(svc.pattern, svc);
}
private final Pattern pattern;
EnumService(final String s)
{
pattern = Pattern.compile(s);
}
public static EnumService returnMockService(String request)
{
for (final Map.Entry<Pattern, EnumService> entry: MAPPING.entrySet())
if (entry.getKey().matcher(request).matches())
return entry.getValue();
return null;
}
}
I would put the strings that you're matching along with the EnumService value they should map to into an array of simple objects, then loop through the array.
E.g.:
ArrayEntry[] entries = new ArrayEntry[] {
new ArrayEntry("/abc*", EnumService.ONE),
// ...and so on...
};
and then:
for (ArrayEntry entry : entries) {
if (request.matches(entry.str)) {
return entry.value;
}
}
return null;
...where ArrayEntry is just a simple class with those two properties (str and value).
If you don't want to have ArrayEntry, you can use a Map:
Map<String,EnumService> entries = new HashMap<String,EnumService>();
entries.put("/abc*", EnumService.ONE);
//...and so on...
and then
for (Map.Entry<String,EnumService> entry : entries.entrySet()) {
if (request.matches(entry.getKey())) {
return entry.getValue();
}
}
return null;
Or you can do it with parallel arrays instead:
String[] strings = new String[] { "/abc*", /*...and so on...*/ };
EnumService[] values = new EnumService[] { EnumService.ONE, /*...and so on...*/ };
and then
int n;
for (n = 0; n < strings.length; ++n) {
if (request.matches(strings[n])) {
return values[n];
}
}
return null;
But parallel arrays tend to be a bit of a maintenance issue.
You can go for design patterns, best for these kind of things are state pattern. State Pattern is to solve this kind of issues and make the code more compatible & flexible. Look over this http://www.javacodegeeks.com/2013/08/state-design-pattern-in-java-example-tutorial.html
Have a look at the below link.
Why can't I switch on a String?
As stated there, if you are using jdk 7, just use switch on the strings. If not, create an enum of /abc*,/bcd*,etc. and use them in switch.
Hope this helps.
Or you could always, store them in an array and loop through them. That would be much easier but costs an extra array.
If there is no logic to map /abc to ONE and /bcd to TWO etc, then maybe you need to load these into an array or map, then simply get the index of the array where it matches.
myMap.put("abc", ONE);
myMap.put("bcd", TWO);
I have one query that is I have used a method but there is many time I have used If Else ..not it become very ambiguous please advise can I use some other conditional loop also..below is my code..
if (cardType == AARP_CARD_TYPE) {
userResponse = messageBox.showMessage("CandidateAARPCardAttachCardToExistingTransaction",
null, IMessageBox.YESNO); // MSG:31.59
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_CANDIDATE_AARP_CARD);
} else if ((cardType == PSC_CARD_TYPE) && ((!PosHelper.isRunningAsService()))) {
userResponse = messageBox.showMessage("PendingPSCCardAttachCardToExistingTransaction", null,
IMessageBox.YESNO); // MSG:31.60
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_PENDING_PSC_CARD);
} else if ((cardType == DR_CARD_TYPE) && ((!PosHelper.isRunningAsService()))) {
userResponse = messageBox.showMessage("PendingDRCardAttachCardToExistingTransaction", null,
IMessageBox.YESNO); // MSG:31.63
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_PENDING_DR_CARD);
} else if ((cardType == WAG_LOYALTY_CARD_TYPE)){
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_NOT_ON_FILE);
if((!PosHelper.isRunningAsService())) {
userResponse = messageBox.showMessage("CardNotOnFileToAttach", null, IMessageBox.YESNO); // MSG:31.32
// BUC
// 1.22.1
}
} else { // If the device is neither of these, POS displays Message 1
// Button, MSG 31.14. [BUC
// 1.23.2]
displayMessage("InvalidLoyaltyCard");
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
NOT_VALID_LOYALTY_CARD);
userResponse = -1;
}
Please advise how can I improve my above logic with some other conditional statements as there is lots n lots of If Else is used..!!
If cardType is an enum, you can add methods to your enum, (say getName, getWag etc.) and call it:
userResponse = messageBox.showMessage(cardType.getMessage(), ...
transaction.setValue(cardType.getWag(), cardType.getRewards());
If it is an int or another non-enum type, you can use a switch as already proposed, or consider switching (haha) to an enum. You could also make PosHelper.isRunningAsService() a boolean parameter to those methods and all your if/else code would be reduced to 3 or 4 lines it seems (although it will introduce some coupling but you seem to have a lot of it already).
Your enum could look like this (simple example that you can complicate as required):
public enum CardType {
AARP_CARD_TYPE {
public String getName() {
return "CandidateAARPCardAttachCardToExistingTransaction";
}
},
PSC_CARD_TYPE {
public String getName() {
return "PendingPSCCardAttachCardToExistingTransaction";
}
};
public abstract String getName();
}
Or more compact, if you don't require complicated logic in the methods:
public static enum CardType {
AARP_CARD_TYPE("CandidateAARPCardAttachCardToExistingTransaction"),
PSC_CARD_TYPE ("PendingPSCCardAttachCardToExistingTransaction");
private final String transactionName;
CardType(String transactionName) {
this.transactionName = transactionName;
}
public String getName() {
return transactionName;
}
}
Use a switch statement instead.
switch (cardType) {
case AARP_CARD_TYPE:
// blah
break;
case PSC_CARD_TYPE:
// blah
break;
// ...
default:
// default blah
break;
}
You have some options: Pattern Strategy, Polymorphism or Events to avoid too much ifs/else
In your example probably the business logic is close to the user interface. You can use the MVC concept to separate the logic from the presentation and reduce the if/elses (if possible).
If you don't like adding methods to CardType as assylias suggested, you can create an 'Action' enum and add the method(s) to that one and use a Map
I have read a lot of topics about code refactoring and avoiding of if else statements. Actually, I have a class where I am using a lot of if - else conditions.
More details: I am using the pull parser and on each line of my soap response, I will check if there is a tag I am interested on, if not, check another tag etc:
if(eventType == XmlPullParser.START_TAG) {
soapResponse= xpp.getName().toString();
if (soapResponse.equals("EditorialOffice")){
eventType = xpp.next();
if (xpp.getText()!=null){
editorialOffice += xpp.getText();
}
}
else if (soapResponse.equals("EditorialBoard")){
eventType = xpp.next();
if (xpp.getText()!=null){
editorialBoard += xpp.getText();
}
}
else if (soapResponse.equals("AdvisoryBoard")){
eventType = xpp.next();
if (xpp.getText()!=null){
advisoryBoard += xpp.getText();
}
}
}
eventType = xpp.next();
}
Now, I would like to use something else, instead of those if else conditions, but I don't know what.
Can you please give me an example?
Try to look at the strategy pattern.
Make an interface class for handling the responses (IMyResponse)
Use this IMyResponse to create AdvisoryBoardResponse, EditorialBoardResponse classes
Create an dictionary with the soapresponse value as key and your strategy as value
Then you can use the methods of the IMyResponse class by getting it from the dictionary
Little Example:
// Interface
public interface IResponseHandler {
public void handleResponse(XmlPullParser xxp);
}
// Concrete class for EditorialOffice response
private class EditorialOfficeHandler implements IResponseHandler {
public void handleResponse(XmlPullParser xxp) {
// Do something to handle Editorial Office response
}
}
// Concrete class for EditorialBoard response
private class EditorialBoardHandler implements IResponseHandler {
public void handleResponse(XmlPullParser xxp) {
// Do something to handle Editorial Board response
}
}
On a place you need to create the handlers:
Map<String, IResponseHandler> strategyHandlers = new HashMap<String,IResponseHandler>();
strategyHandlers.put("EditorialOffice", new EditorialOfficeHandler());
strategyHandlers.put("EditorialBoard", new EditorialBoardHandler());
Where you received the response:
IResponseHandler responseHandler = strategyHandlers.get(soapResponse);
responseHandler.handleResponse(xxp);
In this particular case, since the code is essentially identical for all 3 case except for the String being appended to, I would have a map entry for each of the Strings being built:
Map<String,String> map = new HashMap<String,String>();
map.put("EditorialOffice","");
map.put("EditorialBoard","");
map.put("AdvisoryBoard","");
// could make constants for above Strings, or even an enum
and then change your code to the following
if(eventType == XmlPullParser.START_TAG) {
soapResponse= xpp.getName().toString();
String current = map.get(soapResponse);
if (current != null && xpp.getText()!=null) {
map.put( soapResponse, current += xpp.getText());
}
eventType = xpp.next();
}
No "if... then... else". Not even the added complexity of multiple classes for strategy patterns, etc. Maps are your friend. Strategy is great in some situations, but this one is simple enough to be solved without.
In Java 7 you can SWITCH on Strings. You could use that if you could use that ;-)
Besides zzzzzzz(etc.)'s comment... keep in mind that you are using XmlPullParser which makes you write ugly code like the one you have. You could register some callbacks that would split your code and make it 'better', but if possible, just use SimpleXML library or similar.
Also, you can refactor your code to make it more readable and less verbose. For instance, why do you call xpp.next() inside each if statement? Why not just calling it outside just once:
if(eventType == XmlPullParser.START_TAG) {
soapResponse= xpp.getName().toString();
if (soapResponse.equals("EditorialOffice") && xpp.getText()!=null){
editorialOffice += xpp.getText();
}
else if (soapResponse.equals("EditorialBoard") && xpp.getText()!=null){
editorialBoard += xpp.getText();
}
else if (soapResponse.equals("AdvisoryBoard") && xpp.getText()!=null){
advisoryBoard += xpp.getText();
}
}
eventType = xpp.next();
You could create a ResponseHandler interface with three implementations, one for each branch of your if/else construct.
Then have either a map mapping the different soapResponses to a handler, or a list with all the handler if it can handle that soapResponse.
You also should be able to move some of the boilerplate code to a common possibly abstract implementation of the response handler class.
As so often there a many variations of this. By utilizing the code duplication one actually needs only one implementation:
class ResponseHandler{
String stringToBuild = "" // or what ever you need
private final String matchString
ResponseHandler(String aMatchString){
matchString = aMatchString
}
void handle(XppsType xpp){
if (xpp.getName().toString().equals(matchString){
eventType = xpp.next();
if (xpp.getText()!=null){
editorialOffice += xpp.getText();
}
}
}
}
Your code becomes
List<ResponseHandler> handlers = Arrays.asList(
new ResponseHandler("EditorialOffice"),
new ResponseHandler("EditorialBoard"),
new ResponseHandler("AdvisoryBoard"));
if(eventType == XmlPullParser.START_TAG) {
for(ResponseHandler h : handlers)
h.handle(xpp);
}
vast question that is this one and there is no real answer. (and I don't use soap very often)
here a just some ideas based on your code:
first you can groupe duplicate code
if (soapResponse.equals("EditorialOffice")
||soapResponse.equals("EditorialBoard")
||soapResponse.equals("AdvisoryBoard")){
Another good thing you can do is play around with switch staments like:
switch(soapResponse){
case "EditorialOffice":
case "EditorialBoard":
case "AdvisoryBoard":
eventType = xpp.next();
if (xpp.getText()!=null){
advisoryBoard += xpp.getText();
}
break;
Also you should consider breaking down you test into small functions:
public bool interestingTag(string s){
return (soapResponse.equals("EditorialOffice")
||soapResponse.equals("EditorialBoard")
||soapResponse.equals("AdvisoryBoard"));
}
public processData(xpp){
eventType = xpp.next();
if (xpp.getText()!=null){
editorialBoard += xpp.getText();
}
....}
So that you can just process all your answers in a while loop and you super long if else becomes a 5~10 line function
But as I said there are so many good ways of doing the same thing
You haven't mentioned if you can or do use Java 7. As of that java version you can use Strings in switch statements.
Other than that, encapsulating the logic for each case is a good idea, for example:
Map<String, Department> strategyMap = new HashMap<String, Department>();
strategyMap.put("EditorialOffice", new EditorialOfficeDepartment());
strategyMap.put("EditorialBoard", new EditorialBoardDepartment());
strategyMap.put("AdvisoryBoard", new AdvisoryBoardDepartment());
Then you can simply select the correct strategy from the Map and use it:
String soapResponse = xpp.getName();
Department department = strategyMap.get(soapResponse);
department.addText(xpp.getText());
Department is of course in interface...
You can define an enum like the following:
public enum SoapResponseType {
EditorialOffice(1, "description here") {
public void handle(XmlPullParser xpp) {
//do something you want here
return null;
}
},
EditorialBoard(2, "description here") {
public void handle(XmlPullParser xpp) {
//do something you want here
return null;
}
},
AdvisoryBoard(3, "description here") {
public void handle(XmlPullParser xpp) {
//do something you want here
return null;
}
};
public static SoapResponseType nameOf(String name) {
for (SoapResponseType type : values()) {
if (type.getName().equalsIgnoreCase(name)) {
return type;
}
}
return null;
}
public void handle(XmlPullParser xpp) {
return null;
}
}
Use above enum like this:
SoapResponseType type = SoapResponseType.nameOf("input string");
if (type != null) {
type.handle(xpp);
}
It's clean code, isn't it!