Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 存储库模式和集合错误处理_Design Patterns_Repository Pattern_Service Layer - Fatal编程技术网

Design patterns 存储库模式和集合错误处理

Design patterns 存储库模式和集合错误处理,design-patterns,repository-pattern,service-layer,Design Patterns,Repository Pattern,Service Layer,假设我已获得存储库: interface IRepo { Foo Get(int id); void Add(Foo f); } 现在,有一个要求,我只能有一个具有给定属性的Foo,比如…Id。显然,如果我用一些SQL后端实现IRepo,并将Foo.Id映射到主键,我会免费得到类似的东西-我可能会得到某种PKViolationException。但这正变得与实现相关,所以无论我在哪里使用这个IRepo实现并开始捕获这些异常,我都会失去松耦合的好处 现在,我该如何解决这个问题?我

假设我已获得存储库:

interface IRepo
{
    Foo Get(int id);
    void Add(Foo f);
}
现在,有一个要求,我只能有一个具有给定属性的
Foo
,比如…
Id
。显然,如果我用一些SQL后端实现
IRepo
,并将
Foo.Id
映射到主键,我会免费得到类似的东西-我可能会得到某种
PKViolationException
。但这正变得与实现相关,所以无论我在哪里使用这个
IRepo
实现并开始捕获这些异常,我都会失去松耦合的好处

现在,我该如何解决这个问题?我是否应该添加某种服务层,首先检查对象是否存在,然后抛出一些独立于存储库的异常

class Service
{
    IRepo repo;

    public void AddFoo(Foo foo)
    {
        if(repo.Get(foo.Id) != null)
            repo.Add(foo);
        else
            throw new FooAlreadyExistsException(foo.Id);
    }
}
这个解决方案看起来很糟糕,因为
repo.Add(foo)
仍然会引发一些异常,特别是在检查之后(通过其他一些活动)添加了具有给定Id的对象时,所以我只向我的基础结构添加了一个额外的调用,几乎没有什么好处

似乎我应该小心地实现
IRepo
,并记住这样的异常(可以捕获
PKViolationException
,并在示例SQL实现中将其转换为
fooalReadyExistenceException
),但是如何确保每个
IRepo
实现都遵循这样的规范呢


您通常如何解决这些问题?

首先,考虑以下问题:您更改存储库使用的底层备份存储的可能性有多大(即,从具有主键的数据库更改为不具有唯一ID概念的未知数据存储)?我猜概率非常接近于0。雅格尼适用于这里。不要开始实现代码,以防发生一些不太可能的未知更改。当变更实际发生时,进行必要的变更


那么,关于您的示例,为什么不为要添加的实体生成一个新ID(或者让底层数据存储为您执行),而不是强制客户机代码在之前执行此操作?ID应该使用数据库序列、UUID或自动增量列自动生成。

我首先要说的是

Add(foo)仍然可以抛出一些异常,尤其是当对象 在检查之后(由其他人)添加了具有给定Id的 (活动)

是过度工程的一部分

如果您强烈感觉Add方法必须进行大量验证,那么将您的方法命名为TryAdd类似TryParse。这样,Add方法的调用方就知道Add方法在添加值之前进行验证。

“似乎我应该小心,在实现IRepo时考虑到这样的异常(可以捕获PKViolationException并在示例SQL实现中将其转换为FooAlreadyExistsException)”

这是正确的。抛出的异常成为接口契约的一部分,实现必须遵守此契约。编译器不会为您强制执行此契约,因此您必须完全清楚预期

“但如何确保每个IRepo实现都遵循这样的规范?”

作为接口编写者,您不能对实现它的类负责。如果其他类有缺陷并暴露泄漏的抽象,那就是它们的缺陷。您所能做的就是完全清楚您定义的契约

存储库的目的是抽象出数据库的实现细节,包括其异常

接口IRepo
{
///如果指定的Foo对象已存在于数据库中,则引发。
无效添加(Foo f);
}
类别回购
{
公共无效添加(Foo f)
{
尝试
{
//数据库资料。。。
}
捕获(PKE例外)
{
抛出新的FooAlreadyExistsException(e);
}
}
}