我正在开发一个GWT + Hibernate项目。它由各种模块组成,其中我将命名为两个 - 公司和登录。对于每个模块,我创建了一个RPC服务,这样项目就不会有一个像神一样的服务来完成所有事情。
为了与数据库交互,我使用Hibernate API - 具体来说,是POJO中的EntityManager和Annotations。
另外,我创建了一个Generic 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
更好的建议?
答案 0 :(得分:9)
我认为您可以采取一些措施来改善这一点:
<强>联轴器强>:
大多数时候,组合物比继承更好。由于这种关系,您的服务是强烈耦合的。我的目的是改变它,使用组合代替如下:
public class CompanyService {
private GenericDataAccessService<Company, int> dao; // interface
public void addCompany(Company company) {
dao.save(company);
}
....
}
为了打破依赖关系,dao字段应该是一个抽象类或接口(我既不知道你的代码也不知道你的需要也不知道java),以允许你注入它。
<强> Testeability 强>:
注入这些类的依赖项也可以帮助您测试代码,轻松地为它们注入模拟实例。
不需要的操作:
使用继承强制您拥有可能不想允许的操作。例如,假设您有一个AuditingDataService,如下所示:
public class AuditingService extends GenericDataAccessService<Audit, int>
如果你这样做,你的AuditingService将继承一个删除方法!你能感受到和我一样强烈的气味吗?它将推动您到达以下目的。
<强>反模式强>
在前面的示例(审核日志条目的删除方法)中,您将被强制使用无操作或 throw-exception 方法覆盖它以防止某人使用它...嗯......嗯......这可以是一个好的设计吗?
<强>结论强>:
我认为不是,因为继承不适合这里。
暂时关注LoC并专注于改变设计。
<强>更新强>:
Java有自己的命名约定,如果Impl是一个可怕的后缀,它是每个java程序员理解和共享的约定之一。所以,我认为这没关系。