How to use custom generics to implement the following code, how to optimize

  1. there are two classes with similar structure, both of which are used in the Order class, and the logic in the method is also similar. How to optimize
  2. because Customer and Client are of different types, two methods are written, but the parameters passed are different
  3. can custom generics be implemented, how to write them, or what other ways are available
  4. Thank you
public class Customer {
    private String location;
    private Boolean isDone;
    // get set toString ...
}
public class Client {
    private String location;
    private Boolean isDone;
    // get set toString ...
}
public class Order{

    public void execute_customer(Customer customer){
        if(customer.getDone()){
            customer.setLocation("hongkong");
        }
        System.out.println("result: "+ customer);
    }

    public void execute_client(Client client){
        if(client.getDone()){
            client.setLocation("hongkong");
        }
        System.out.println("result: " + client);
    }
}
=============================  =====================================

public List<Customer> retrieveEntityRecords(RequestParam request) {
      
        List<String> ids = request.getId();
        String location = request.getLocation();
        
        boolean idsCk = CollectionUtils.isNotEmpty(ids) && ids.size() > 0;
        boolean locationCk = !StringUtils.isEmpty(location);
        
        List<Customer> result;
        try {
            if (idsCk) {
                if (locationCk) {
                    // use [ids location] query db
                    result=customerRepository.findxxx(ids,location);
                } else {
                   // use [ids] query db
                   result=customerRepository.findxxx(ids);
                }
            } else {
                if (locationCk) {
                    // use [location] query db
                    result=customerRepository.findxxx(location);
                } else {
                   // query all record in db
                   result=customerRepository.findxxx();
                }
            }
        } catch (Exception e) {
            throw new SystemException();
        }
        
        return result;
    }
    
    
public List<Client> retrieveEntityRecords(RequestParam request) {
      
        List<String> ids = request.getId();
        String location = request.getLocation();
        
        boolean idsCk = CollectionUtils.isNotEmpty(ids) && ids.size() > 0;
        boolean locationCk = !StringUtils.isEmpty(location);
        
        List<Client> result;
        try {
            if (idsCk) {
                if (locationCk) {
                    // use [ids location] query db
                    result=clientRepository.findxxx(ids,location);
                } else {
                   // use [ids] query db
                   result=clientRepository.findxxx(ids);
                }
            } else {
                if (locationCk) {
                    // use [location] query db
                    result=clientRepository.findxxx(location);
                } else {
                   // query all record in db
                   result=clientRepository.findxxx();
                }
            }
        } catch (Exception e) {
            throw new SystemException();
        }
        
        return result;
    }

Thank you for inviting
Let's start with something simple and rude. It is very simple to want to combine the two methods into one. In fact, when you are in one, the public parts are represented by the same thing. There are already brothers upstairs who have suggested using interfaces, which is OK, but the attributes are similar to the code structure. lambda+ generics should be able to solve

.

We can see that there are only two

where the two methods need to be normalized.
  1. customer.getDone () and client.getDone ()
  2. customer.setLocation ("hongkong") and client.setLocation ("hongkong")
The corresponding methods of

these two places should be Predicate and Consumer , respectively, so we can establish such a public method execute

.
public <T> void execute(T t, Predicate<T> donePredicate, Consumer<T> locationConsumer){
       if(donePredicate.test(t)){
           locationConsumer.accept(t);
       }
       System.out.println("result: "+ t);
}

so the previous two methods execute_customer and execute_client can be changed to

.
public void execute_customer(Customer customer){
       this.execute(customer, Customer::getDone, c -> c.setLocation("hongkong"));
}

public void execute_client(Client client){
       this.execute(client, Client::getDone, c -> c.setLocation("hongkong"));
}

but it is not recommended that you write this. After all, the method of three parameters is still very long. And Customer and Client may not have any business connection themselves, so forcibly integrating the code will only bring huge understanding costs to future maintenance, because they just happen to have two identical fields, and their processing patterns happen to be the same

.

but we can think about it another way. Since they happen to have two identical fields and their processing mode happens to be the same, and the subject also wants to combine them in the same method, then we don't look at Customer and Client , but just look at the attributes location and isDone . Can we just look at the processing of if in the method to show whether their two attributes together represent a business? A service independent of Customer and Client has its own attributes location and isDone , as well as its own behavior, that is, the processing of if . If we set such a service as Location , it must be written as

.
public class Location {
       private String location;
       private Boolean isDone;
       // get set toString ...

       public void execute(){
            if(this.getDone()){
                this.setLocation("hongkong");
            }
        }
}

in this case, Customer and Client become

public class Customer {
       private Location location;
}

public class Client {
       private Location location;
}

so the method in Order becomes

public void execute_customer(Customer customer){
       customer.getLocation().execute();
}

public void execute_client(Client client){
       client.getLocation().execute();
}

mm-hmm, to put it this way, the perfect yuppie, of course, everything is decided by the actual business. I just made some guesses based on the information provided by the current subject, and wrote a way that the actual business may need to be adjusted for reference only.

= small updates =

in the comments, the subject mentioned how to integrate with interfaces. In fact, this is also a small example of interface-oriented programming, ha. Interface-oriented programming means that the entire business logic needs to be assembled in a way predefined by the interface. The place where the interface is called, that is, the point of business abstraction

is actually about the same as the previous abstract point, or

  1. customer.getDone () and client.getDone ()
  2. customer.setLocation ("hongkong") and client.setLocation ("hongkong")

these two points, previously expressed by functional interfaces, are represented by interfaces. For example, if we define such a functional interface, let's call it ILocation , then it can be expressed in this way

.
public interface ILocation{
    Boolean getDone();
    void setLocation(String location);
}
It just so happens that the two methods are named set and get methods in Client and Customer , thus reducing the need to write implementation methods, and then Client and Customer can express

in this way.
@Getter
@Setter
public class Client implements ILocation{
    private String location;
    private Boolean isDone;
}

@Getter
@Setter
public class Customer implements ILocation{
    private String location;
    private Boolean isDone;
}

because both the set and get method methods have been implemented ILocation , after using the comments of lombok , the code can add nothing (lombok's get method for the default Boolean type attribute is getIsDone , so do not add is beginning if necessary)

After

, you can write a common method in Order .

public void execute(ILocation location){
    if(location.getDone()){
       location.setLocation("hongkong");
    }
}
The implementation of

interface is roughly like this, which is for reference only.

= 2018-11-21 New question Update =
before answering the question, it is important to make it clear that any code integration, either within the scope of the business can be merged, or public structure or tool extraction, excessive forced integration of code, will only arbitrarily increase the coupling between businesses, bringing great stability to the subsequent expansion development

going back to the new problem of the subject, we can see that it is no longer simply solved by passing in parameters (although it is still possible, but it is not very friendly and not conducive to expansion). This time we can simply abstract such a business process from the direct two-segment method

public <T> List<T> retrieveEntityRecords(RequestParam request) {

        List<String> ids = request.getId();
        String location = request.getLocation();

        boolean idsCk = CollectionUtils.isNotEmpty(ids) && ids.size() > 0;
        boolean locationCk = !StringUtils.isEmpty(location);

        List<T> result;
        try {
            if (idsCk) {
                if (locationCk) {
                    // use [ids location] query db
                } else {
                    // use [ids] query db
                }
            } else {
                if (locationCk) {
                    // use [location] query db
                } else {
                    // query all record in db
                }
            }
        } catch (Exception e) {
            throw new SystemException();
        }

        return result;
    }

the above code should be the backbone code of the previous two methods, and it is also the core code of the business to be integrated this time. Let's take the common part away from the last question and deal with it, ah. Other specific implementation needs to be implemented with interfaces, and what needs to be implemented is that these four lines are annotated

.
// use [ids location] query db
// use [ids] query db
// use [location] query db
// query all record in db

so we define such an interface ILocationIdsQuery (personal understanding is the common logic of querying according to Location and id, so it is so simply named), it defines four abstract methods

.
public interface ILocationIdsQuery<T> {
    List<T> queryByIdsAndLocation(List<String> ids, String location);
    List<T> queryByIds(List<String> ids);
    List<T> queryLocation(String location);
    List<T> query();
}
When

is defined in this way, you will find that this is consistent with the interface methods of customerRepository and clientRepository . If your customerRepository and clientRepository do not have a common parent interface, then you can add a parent interface to both of them ILocationIdsQueryExecutor and supplement their implementation. If customerRepository and clientRepository have such a common parent interface,

once you have such a public interface, it's easy to open a utility class LocationIdsQueryUtils (keep business consistency and make it easier for others to read the code, so it's just a name)

public class LocationIdsQueryUtils {

    public static <T> List<T> doQuery(ILocationIdsQuery<T> locationIdsQuery, RequestParam request){
        List<String> ids = request.getId();
        String location = request.getLocation();

        boolean idsCk = CollectionUtils.isNotEmpty(ids) && ids.size() > 0;
        boolean locationCk = !StringUtils.isEmpty(location);

        List<T> result;
        try {
            if (idsCk) {
                if (locationCk) {
                    // use [ids location] query db
                    result = locationIdsQuery.queryByIdsAndLocation(ids, location);
                } else {
                    // use [ids] query db
                    result = locationIdsQuery.queryByIds(ids);
                }
            } else {
                if (locationCk) {
                    // use [location] query db
                    result = locationIdsQuery.queryByLocation(location);
                } else {
                    // query all record in db
                    result = locationIdsQuery.queryAll();
                }
            }
        } catch (Exception e) {
            throw new SystemException();
        }
        return result;
    }
}

now that you have such a tool class, the rest is much easier to write. You can call the tool class directly

.
public List<Customer> retrieveEntityRecords(RequestParam request) {
        List<Customer> result = LocationIdsQueryUtils.doQuery(customerRepository, request);
        return result;
}
    
public List<Client> retrieveEntityRecords(RequestParam request) {
        List<Client> result = LocationIdsQueryUtils.doQuery(clientRepository, request);
        return result;
}    
    

this is roughly the case. For reference only, it will be paste

.

if you use classes, you can use reflect,
to optimize performance, you can use dome4j, to read xml configuration files, and
is similar to spring's ioc container to achieve a certain degree of performance optimization
simple demo forget to adopt
if you use interfaces, consider using decorator mode

public class Order
{
    public void execute_object(String className){
        try
        {
            if(className.equals("Customer")){
                //className:
                Class forName = Class.forName(className);
                Customer customer=(Customer) forName.newInstance();
                if(customer.getIsDone()){
                    customer.setLocation("hongkong");
                }
                System.out.println("result: "+ customer);
            }
            //className:
            Class forName = Class.forName(className);
            Client client=(Client) forName.newInstance();
            if(client.getIsDone()){
                client.setLocation("hongkong");
            }
            System.out.println("result: " + client);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
    }
    public static void main(String[] args)
    {
        Order order = new Order();
        order.execute_object("Customer");
    }
}

is it better to use an interface?


there is no common part between your two objects, so it is not easy for you to use a common object to implement the extraction.

Menu