怎么用自定义泛型实现下面的代码,怎么优化

  1. 有两个类 结构差不多,都在Order类里面用到,而且方法里面逻辑也差不多,怎么优化
  2. 因为Customer和Client不同类型,所以写了两个方法,只是传的参数不一样
  3. 自定义泛型可以实现么,怎么写比较好,或者其它有什么方式
  4. 谢谢
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;
    }
阅读 3.7k
4 个回答

谢邀
先来简单粗暴的。。。。竟然想把两个方法合一,很简单,其实合一的时候,就是把公共部分用同一个东西表示,楼上已有兄弟有建议用接口,是可以的,不过竟然属性和代码结构差不多,lambda+泛型应该就可以解决

我们可以看到,那两个方法,需要归一的地方就两个

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

这两个地方的对应的方法分别就应该是PredicateConsumer了,所以我们可以建立这么一个公共方法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);
}

所以之前的两个方法execute_customerexecute_client就可以改为

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"));
}

不过不建议你这么写。。。毕竟三个参数的方法,还是很长了。。。并且CustomerClient在业务上本身可能也没有啥联系,强行整合代码,只会给以后的维护带来巨大理解成本,因为它们只是恰好有两个相同的字段,恰好他们的处理模式一样

但是我们可以换个角度思考,既然恰好它们有两个相同的字段,恰好他们处理模式一样,题主也想把他们合在一个方法里,那我们不看CustomerClient,光看属性locationisDone,光看方法里if的处理,是不是就可以说明,他们两个属性合在一起代表着一个业务,一个独立于CustomerClient的业务,它有自己的属性locationisDone,也有自己的行为,那就是if的处理,假如我们设定这么一个业务叫Location,那它一定可以写成

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

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

这样的话,CustomerClient就变成

public class Customer {
       private Location location;
}

public class Client {
       private Location location;
}

因此Order里的方法就变成

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

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

嗯嗯,这么写,赶脚完美的雅痞,当然一切还是以实际业务来决定,我只是根据当前题主提供的信息做了一些猜想,并且写出的一种方式,实际业务可能需要调整,仅供参考,就酱

===============================小小更新==============================

评论里题主提到如何用接口来整合,其实这也是面向接口编程的小实例哈,面向接口编程,就是需要把整个业务逻辑按照接口预定义的方式组装起来,出现接口的调用的地方,即业务抽象的点

其实和之前的抽象点差不多,还是

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

这两个点之前用函数式接口表示的,此时用接口去表示,比如我们定义这样一个功能接口,就叫ILocation吧,那它可以这么表示

public interface ILocation{
    Boolean getDone();
    void setLocation(String location);
}

为什么要这么写,是恰好,这两个方法的命名方式就是ClientCustomer中的属性setget方法,这样就减少了写实现方法的必要,之后ClientCustomer就可以这么表示

@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;
}

因为setget方法方法都已经实现了ILocation,用了lombok的注解后,代码可以啥都不加了(lombok对于默认的Boolean类型属性的get方法是getIsDone,所以若是需要的话,不要加is开头)

之后在Order那里就可以这么写一个公用方法

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

接口实现大体就是这样,仅供参考,就酱

=============================2018-11-21新问题更新===============================
回答问题之前,还是要说明一点哈,任何代码的整合,要么是要在业务范围内可以合并,要么就是公共结构或工具抽取,过分强行的整合代码,只会胡乱增加业务之间的耦合,给后续扩展开发带来极大的不变

回到题主的新问题中,可以看到,这个时候已经不是简单靠传入参数就解决了(虽然也还是可以,不过那么写实在不太友好,也不利于扩展),这次我们可以从直接两段方法中简单抽象出这么一个业务过程

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;
    }

以上这段代码应该就是之前两个方法的主干代码了,也是这次要整合的业务核心代码,我们还是学上个问题一样,把公共部分抽离处理啊,其他需要具体实现的那就用接口来实现了,那具体需要实现的就是这4行注释了

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

那所以我们定义这么一个接口ILocationIdsQuery(个人理解就是按照Location和id进行查询的公共逻辑,所以这么简单取名了),它就定义有这么4个抽象方法

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();
}

这么定义下来,就会发现其实这和customerRepositoryclientRepository的接口方法一致,如果你的customerRepositoryclientRepository没有公共的父类接口,那么这个时候你可以给它们都加个一个父类接口ILocationIdsQueryExecutor,并补充他们的实现,如果本身customerRepositoryclientRepository就有这么一个公共的父类接口,那就可以不用定义这么一个新接口了

有了这么一个公共接口之后,那就简单了,直接开一个工具类LocationIdsQueryUtils(保持业务的一致性,方便其他人看看代码,所以就取了这么一个名字)

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;
    }
}

那有了这么一个工具类,剩下的就好写多了,直接调用工具类即可

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;
}    
    

大体就是这样,仅供参考,就酱

如果使用类,可以使用reflect,
性能上优化,可以使用dome4j,读取xml配置文件,
类似于spring的ioc容器,达到一定程度上的性能优化
简单的demo忘题主采纳
如果使用接口,可以考虑采用装饰者模式

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");
    }
}

用接口更好吧?

你两个对象之间没有建立公共的部分,你想抽取用一个公共对象来实现,是不好搞的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题