Home > database >  Is there a more concise way of writing repetitive code changing which method is used?
Is there a more concise way of writing repetitive code changing which method is used?

Time:01-21

I've written a very repetitive piece of code (as seen below) and I feel that there has to be some way to use a method to make it more concise, but I cannot figure out how.

This is the current code snippet:

if (event.getSource() == firstNameButton) {
    inputType = "first name";
    for (int i = 0; i < myList.length; i  ) {
        if (myList[i].getFirstName().equals(input)) {
            returnList[returnLength] = myList[i];
            returnLength  ;
        }
    }
} else if (event.getSource() == lastNameButton) {
    inputType = "last name";
    for (int i = 0; i < myList.length; i  ) {
        if (myList[i].getLastName().equals(input)) {
            returnList[returnLength] = myList[i];
            returnLength  ;
        }
    }
} else if (event.getSource() == usernameButton) {
    inputType = "username";
    for (int i = 0; i < myList.length; i  ) {
        if (myList[i].getUsername().equals(input)) {
            returnList[returnLength] = myList[i];
            returnLength  ;
        }
    }
} else if (event.getSource() == domainNameButton) {
    inputType = "domain name";
    for (int i = 0; i < myList.length; i  ) {
        if (myList[i].getDomainName().equals(input)) {
            returnList[returnLength] = myList[i];
            returnLength  ;
        }
    }
} else if (event.getSource() == domainExtensionButton) {
    inputType = "domain extension";
    for (int i = 0; i < myList.length; i  ) {
        if (myList[i].getDomainExtension().equals(input)) {
            returnList[returnLength] = myList[i];
            returnLength  ;
        }
    }
}

The only things changing are what inputType gets assigned to, which I would be able to write a method for, but I am also changing what method I apply to myList[i]. Is there a way to write a method with a method parameter?

CodePudding user response:

Assume that myList is an array of class MyList.
Add the following method to class MyList.

public class MyList {
    public String getValue(String key) {
        return switch (key) {
                   case "first name" -> getFirstName();
                   case "last name" -> getLastName();
                   case "username" -> getUsername();
                   case "domain name" -> getDomainName();
                   case "domain extension" -> getDomainExtension();
               }
    }
}

The above method uses switch expressions which were added in Java 12.

Assume the code in your question is from an actionPerformed method.
Assume that firstNameButton, lastNameButton, etc, are all JButtons.
Then you can assign an appropriate action command to each, for example:

firstNameButton.setActionCommand("first name");

Now your actionPerformed method can be reduced to:

public void actionPerformed(java.awt.event.ActionEvent event) {
    String actionCommand = event.getActionCommand();
    for (int i = 0; i < myList.length; i  ) {
        if (myList[i].getValue(actionCommand).equals(input)) {
            returnList[returnLength] = myList[i];
            returnLength  ;
        }
    }
}

Alternatively, you can use the stream API:

public void actionPerformed(java.awt.event.ActionEvent event) {
    String actionCommand = event.getActionCommand();
    java.util.Arrays.stream(myList)
                    .filter(m -> m.getValue(actionCommand).equals(input))
                    .findFirst()
                    .ifPresent(m -> {
                                   returnList[returnLength] = myList[i];
                                   returnLength  ;
                               });
}

Note that method findFirst returns an object of type Optional.

CodePudding user response:

Create an abstraction for extracting data from your object. You need something which accepts argument of type your custom class and return string result, Function will do, but you can create your own as well. Extract the repetition to a method, using the abstraction:

private void myMethod(MyObject[] myList, Function<MyObject, String> valueExtractor, String input) {
  for (int i = 0; i < myList.length; i  ) {
    if (valueExtractor.apply(myList[i]).equals(input)) {
      //do required stuff
    }
  }
}

You can still use if-else to get the correct extractor, but i would suggest to use a Map instead to keep the keys and their respective extraction functions:

Map<String, Function<MyObject, String>> map = Map.of("firstNameButton", MyObject::getFirstName, 
  "lastNameButton", MyObject::getLastName);
Function<MyObject, String> extractor = map.get(event.getSource());
myMethod(theList, extractor, theInput);

CodePudding user response:

Here is an "Old Dog" approach:

Repeated or nearly repeated sections of code can be often be reduced with arrays and / or Collection Objects, loops, and methods. What parts of the nearly repeated code are the same, and what parts are different?

  if (event.getSource() == *********Button) {
      inputType = "**********";
      for (int i = 0; i < myList.length; i  ) {
          if (myList[i].get*********().equals(input)) {
              returnList[returnLength] = myList[i];
              returnLength  ;
    }
}

I replaced the parts of the O/P code that vary with *********.

I have to make some assumptions here.

The array myList is apparently an array of some sort of Object, with various geter methods that return some sort of Object, probably a String. But, I'll use Bar in this example:

Suppose myList is an array of Foo. The beginning of the code for Foo might look something like this:

 public class Foo { 
    private Bar firstName, lastName, userName, domainName, domainExtension;  

You could add this method:

 public Bar getNameAttribute (int attributeKey) { 
   switch (attributeKey) { 

     case 0: 
       return firstName;
       break;
    case 1: 
       return lastName;
       break;
    case 2:
       ...
    default:
       return null;
       break;
   } 
}

Now, the various ~Button variables are reference types. It's reasonable to assume that, once they are created and initialized, the references will not be changed to point to some other button. In fact, use of event.getSource () == implies that is the case. So, let us use an array to reference them, in addition to referencing them using their individual reference names. I'll assume they are Objects of JButton type:

 JButton [] nameButton = { firstNameButton, lastNameButton, userNameButton
            , domainNameButton, domainExtensionButton };
 String [] eventType = {"first  name", "last name", "user name"
            , "domain name", "domain extension" };
          

  for (int k = 0; k < nameButton.length; k  ) { 
     if (event.getSource() == nameButton [k] { 
        for (int i = 0; i < myList.length; i  ) { 
           if (mylList[i].getNameAttribute[k].equals(input)){
              returnList[returnLength] = myList[i];
              returnLength  ;
           }
        }
        break;
     }
  }

Notes:

Use of switch in the getNameAttribute method assumes references to the various names can be changed. For example, Foo foo1 = new Foo (...); ... foo1.setFirstName (new Bar ("Jones")); If, after creation and initialization, references to all of the names are fixed, the getNameAttribute code can be reduced by using an array of aliases, as was done with the Buttons.

  • Related