通用DAO模式实现设计

7
我正在处理一个GWT+Hibernate项目。它由多个模块组成,其中我将命名两个——公司和登录。为了每个模块,我创建了一个RPC服务,以便项目不会以一个神一样的服务结束。
为了与数据库交互,我使用Hibernate API——具体来说,是POJO中的EntityManager和Annotations。
此外,我创建了一个通用的DAO类来处理基本的CRUD操作。GenericDAO类还将处理EntityManager。每个模块服务类都将扩展此GenericDAO,以便可以添加自己的查询方法。
以下是GenericDAO类的存根 -
public class GenericDataAccessService<EntityType, PrimaryKeyType extends Serializable> {

    // Constructor

    @Override
    public void save(EntityType newEntity) {
        // TODO Auto-generated method stub
    }

    @Override
    public void update(EntityType entity) {
        // TODO Auto-generated method stub

    }

    @Override
    public EntityType find(PrimaryKeyType primaryKey) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<EntityType> findByProperty(String property) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<EntityType> findAll() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void delete(PrimaryKeyType primaryKey) {
        // TODO Auto-generated method stub

    }

    @Override
    public void delete(EntityType entity) {
        // TODO Auto-generated method stub

    }
}

现在,假设我要为公司模块添加一个查找方法。因此,我编写了如下的CompanyService类 -

public class CompanyService extends GenericDataAccessService<Company, int> {

    public void addCompany(Company company) {
        super.save(company);
    }

    public Company updateCompany(Company company) {
        return super.update(company);
    }

    public List<Company> findMatchingCompanies(String companyName) {
        return super.findByProperty(companyName);
    }

    public void deleteCompany (int companyId) {
        super.delete(companyId);
    }

    public List<Company> findThroughSomeCustomSearch() {
        // custom query code.
    }
}

其他模块也是类似的步骤。这样,我还可以为每个模块的服务添加非数据访问相关的方法。

现在,我需要在客户端暴露这些模块。我选择不以任何方式暴露GenericDAO类;因此没有接口。相反,我为每个模块创建一个接口。

所以,对于CompanyService,它是 -

public interface CompanyService {

    public void addCompany(Company company);

    public Company updateCompany(Company company);

    public List<Company> findMatchingCompanies(String companyName);

    public void deleteCompany (int companyId);

    public List<Company> findThroughSomeCustomSearch();
}

其他模块的接口也是同样的方式。
这是一个好的设计吗?GenericDAO 确实节省了一些会话管理和基本 CRUD 操作的样板代码。然而,由于 Hibernate API 的存在,每个方法已经只剩下 3-4 行代码。在这种情况下,我没有发现 GenericDAO 的其他用途。或者我使用的方式有问题吗?
如果这种设计不够好,请提出更好的建议。
编辑: 我想知道在这种情况下你会给服务模块什么名称。我现在使用 "Impl" 后缀,但感觉有点不顺口。例如,对于公司模块, 接口 - CompanyService 类 - CompanyServiceImpl
有更好的建议吗?
1个回答

9
我认为有几个方面可以改进: 耦合性
大多数情况下,组合优于继承。由于这种关系,您的服务之间具有强耦合性。我建议改变它,使用组合代替如下:
public class CompanyService  {
    private GenericDataAccessService<Company, int> dao; // interface 

    public void addCompany(Company company) {
        dao.save(company);
    }
    ....
}

为了打破依赖性,dao字段应该是一个抽象类或接口(我既不知道你的代码也不知道你的需求和Java),以允许您注入它。
可测试性:
注入这些类的依赖项还将帮助您轻松地测试代码,注入mock实例即可。
不需要的操作:
使用继承会强制您拥有可能不想允许的操作。例如,想象一下您有一个AuditingDataService如下:
public class AuditingService extends GenericDataAccessService<Audit, int> 

如果你这样做,你的AuditingService将继承一个删除方法!!!你能感觉到那股强烈的味道吗?它会让你走向以下这个点。 反模式 在之前的例子中(用于审计日志条目的删除方法),你将被迫用一个什么都不做抛出异常的方法来覆盖它,以防止有人使用它... 呃... 这是一个好的设计吗? 结论
我认为不是,因为继承在这里并不适用。
暂且忘记代码行数,专注于改变设计。 更新:
Java有其自己的命名约定,如果Impl是一个糟糕的后缀,但它是每个Java程序员都理解和共享的约定之一。所以,我认为这是可以的。

谢谢你的回答。昨天我没有得到答案后,我仔细思考了一下,并得出了使用组合而不是继承的结论。此外,我在考虑是否要在ModuleService中使用接口来保存GenericDao对象。我想这将是更好的设计。坚持减少代码行数不应该是首要任务。一旦我把这个想法放在脑后,我就想到了组合和编码接口。你的回答为我确认了这一点。谢谢! - bhootjb
我已经编辑了我的帖子,添加了另一个问题。请看一下,并指导我。 - bhootjb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接