Advantage of using Interface over abstract class for repository pattern?
Possible Duplicate:
Interface vs Base class
通常会看到使用接口实现的存储库模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public interface IFooRepository { Foo GetFoo(int ID); } public class SQLFooRepository : IFooRepository { // Call DB and get a foo public Foo GetFoo(int ID) {} } public class TestFooRepository : IFooRepository { // Get foo from in-memory store for testing public Foo GetFoo(int ID) {} } |
但是您也可以使用抽象类来实现这一点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public abstract class FooRepositoryBase { public abstract Foo GetFoo(int ID); } public class SQLFooRepository : FooRepositoryBase { // Call DB and get a foo public override Foo GetFoo(int ID); {} } public class TestFooRepository : FooRepositoryBase { // Get foo from in-memory store for testing public override Foo GetFoo(int ID); {} } |
在存储库场景中,与抽象类相比,使用接口的具体优势是什么?
(也就是说,不要只是告诉我您可以实现多个接口,我已经知道了这一点——为什么要在存储库实现中这样做)
编辑以澄清-"msdn-在类和接口之间选择"等页面可以解释为"选择类而不是接口,除非有充分的理由不这样做"-在存储库模式的特定情况下,什么是好的理由
在这个实例中,与抽象类相比,使用接口的主要优点是接口是完全透明的:这是一个更大的问题,您无法访问继承的类的源。
但是,这种透明性允许您生成一个已知范围的单元测试:如果您测试一个接受接口作为参数的类(使用依赖项注入方法),您就知道您正在用已知的数量测试该类;接口的测试实现将只包含您的测试代码。
同样,当测试存储库时,您知道您只测试存储库中的代码。这有助于限制测试中可能的变量/交互的数量。
就我个人而言,我倾向于有一个接口来保存纯"业务相关"的方法的签名,例如
我在抽象的
除了拥有水管厂的另一个优势外,我还有一个优势,例如,我有一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public abstract class Repository<T> { private IObjectSet objectSet; protected void Add(T obj) { this.objectSet.AddObject(obj); } protected void Delete(T obj) { this.objectSet.DeleteObject(obj); } protected IEnumerable<T>(Expression<Func<T, bool>> where) { return this.objectSet.Where(where); } } public interface IFooRepository { void DeleteFoo(Foo foo); IEnumerable<Foo> GetItalianFoos(); } public class FooRepository : Repository<Foo>, IFooRepository { public void DeleteFoo(Foo foo) { this.Delete(foo); } public IEnumerable<Foo> GetItalianFoos() { return this.Find(foo => foo.Country =="Italy"); } } |
使用抽象类而不是接口进行水管工程的好处是,我的具体存储库不必实现它们不需要的方法(例如,
对于业务模型,使用接口比使用抽象类的优势在于,接口提供了如何从业务端操作
这是一个适用于任何类层次结构的一般问题,而不仅仅是存储库。从纯OO的角度来看,接口和纯抽象类是相同的。
如果您的类是公共API的一部分,那么使用抽象类的主要优点是您可以在将来添加方法,而不会有破坏现有实现的风险。
有些人还喜欢将接口定义为"类可以做的事情",而将基类定义为"类是什么",因此只将接口用于外围功能,并始终将主要功能(如存储库)定义为类。我不知道我站在哪里。
为了回答您的问题,我认为在定义类的主要函数时使用接口没有任何好处。
由于模式起源于域驱动的设计,这里有一个DDD答案:
存储库的契约通常在域层中定义。这允许域和应用程序层中的对象操作存储库的抽象,而不必关心它们的实际实现和底层存储细节——换句话说,就是说,这些对象对持久性一无所知。此外,我们通常希望在某些存储库的契约中包含特定的行为(除了您的普通add()、getbyid()等),因此我更喜欢
另一方面,存储库的具体实现驻留在基础结构层(或测试存储库的测试模块中)。它们实现了上述存储库契约,但也有它们自己的持久性特定特性范围。例如,如果您正在使用nhibernate来持久化您的实体,那么使用nhibernate会话和其他nhibernate相关的通用管道将一个超类放到所有nhibernate存储库中就可以派上用场。
由于您不能继承几个类,所以最终的具体存储库继承的这两个类之一必须是一个接口。
域层契约是一个接口(
持久性特定的存储库可以是一个抽象类(基础结构层中的
结果是:
1 2 3 4 | public class SomeEntityRepository : NHibernateRepository<SomeEntity>, ISomeEntityRepository { //... } |
我想关键的区别是,抽象类可以包含私有属性和方法,其中接口不能,因为它只是一个简单的约定。
作为接口的结果总是"这里没有恶作剧-你看到的就是你得到的",而抽象的基类可能会允许副作用。
虽然从纯实际的角度来看,其他框架可能需要添加更多内容,但大多数IOC框架在接口->类映射方面工作得更好。您可以在接口和类上具有不同的可见性,而对于继承,可见性必须匹配。
如果你不使用国际奥委会的框架,从我的角度来看没有什么区别。提供程序基于抽象基类。
看看TimMcCarthy的存储库框架的实现。
他使用像
因此,您可以将这两种方法结合起来使用它们的优势。
另外,我认为大多数IOC框架都可以处理抽象类。