如何多态控制不同接口类对象?不可修改Dao类时多态调用search方法
这是个很典型的「接口不统一但需要多态调用」的场景,既然不能修改原有Dao的源码,适配器模式(Adapter Pattern) 绝对是你的最佳选择——它就是专门用来解决这种「现有类接口不符合需求,但又不能改源码」的问题的。
下面给你一步步拆解实现思路:
1. 定义统一的目标接口
首先,我们需要先定一个所有Dao都要适配到的统一接口,比如叫Searchable,里面定义你期望的统一search方法签名。你可以根据实际业务调整参数和返回值,举个参考例子:
public interface Searchable { // 统一的搜索方法,用自定义查询条件类兼容不同Dao的参数,返回统一结果封装 SearchResult search(SearchCondition condition); }
这里的SearchCondition是你自己定义的通用查询条件类,用来适配各个Dao的不同查询参数;SearchResult则是通用结果封装类,用来统一不同Dao的返回类型。
2. 为每个现有Dao编写适配器类
接下来,为每个Dao(比如AaaDao、BbbDao...ZzzDao)写对应的适配器类,让它实现Searchable接口,同时持有对应Dao的实例,在适配器的search方法里完成「统一条件转Dao自有参数」和「Dao返回结果转统一结果」的逻辑。
举两个具体的适配例子:
例子1:适配AaaDao
假设AaaDao的原有搜索方法是:
// 原有AaaDao的接口(无法修改) public class AaaDao { public List<Aaa> findByKeyword(String keyword, int pageSize) { // 原有实现逻辑 } }
对应的适配器类:
public class AaaDaoAdapter implements Searchable { // 持有原有Dao的实例 private AaaDao aaaDao; // 通过构造器注入原有Dao对象 public AaaDaoAdapter(AaaDao aaaDao) { this.aaaDao = aaaDao; } @Override public SearchResult search(SearchCondition condition) { // 1. 把统一的SearchCondition转换成AaaDao需要的参数 String keyword = condition.getKeyword(); int pageSize = condition.getPageSize(); // 2. 调用原有Dao的搜索方法 List<Aaa> aaaList = aaaDao.findByKeyword(keyword, pageSize); // 3. 把Aaa的结果转换成统一的SearchResult SearchResult result = new SearchResult(); result.setData(aaaList); result.setTotal(aaaList.size()); return result; } }
例子2:适配BbbDao
假设BbbDao的原有搜索方法是另一种签名:
// 原有BbbDao的接口(无法修改) public class BbbDao { public Bbb queryByIdAndName(Long id, String name) { // 原有实现逻辑 } }
对应的适配器类:
public class BbbDaoAdapter implements Searchable { private BbbDao bbbDao; public BbbDaoAdapter(BbbDao bbbDao) { this.bbbDao = bbbDao; } @Override public SearchResult search(SearchCondition condition) { // 转换参数 Long id = condition.getEntityId(); String name = condition.getEntityName(); // 调用原有方法 Bbb bbb = bbbDao.queryByIdAndName(id, name); // 转换结果 SearchResult result = new SearchResult(); if (bbb != null) { result.setData(Collections.singletonList(bbb)); result.setTotal(1); } else { result.setData(Collections.emptyList()); result.setTotal(0); } return result; } }
3. 多态调用统一接口
现在,你就可以把所有适配器当作Searchable接口的实现类来多态使用了:
public class Client { public static void main(String[] args) { // 初始化各个Dao和对应的适配器 Searchable aaaSearcher = new AaaDaoAdapter(new AaaDao()); Searchable bbbSearcher = new BbbDaoAdapter(new BbbDao()); // ... 其他ZzzDao的适配器 // 把所有搜索器放到统一的集合里 List<Searchable> searchers = Arrays.asList(aaaSearcher, bbbSearcher); // 构造统一的查询条件 SearchCondition condition = new SearchCondition(); condition.setKeyword("test"); condition.setPageSize(10); condition.setEntityId(1L); condition.setEntityName("demo"); // 多态调用search方法! for (Searchable searcher : searchers) { SearchResult result = searcher.search(condition); // 处理统一的结果 System.out.println("搜索结果总数:" + result.getTotal()); } } }
备选方案:反射(不推荐)
如果你觉得写几十个适配器太麻烦,也可以用反射来调用不同的search方法,但这种方式类型不安全,而且代码可读性差,后期维护成本高,只有在适配器数量极多且逻辑非常简单的情况下才考虑。比如:
public Object searchWithReflection(Object dao, SearchCondition condition) throws Exception { // 根据dao的类型获取对应的方法名和参数 Method method = dao.getClass().getMethod("findByKeyword", String.class, int.class); return method.invoke(dao, condition.getKeyword(), condition.getPageSize()); }
但还是强烈推荐适配器模式,它是类型安全的,而且逻辑清晰,后期扩展新Dao的时候只需要加一个新的适配器即可,完全符合开闭原则。
内容的提问来源于stack exchange,提问作者Tando

