Sco*_*ott 12 c# oop abstraction solid-principles
在我们的C#MVC应用程序中,我们有很多接口,它们将实现它们的对象映射为1到1.即:基本上,对于创建的每个对象,已经执行了"提取界面"操作.
Moq使用这些接口为我们的单元测试生成模拟对象.但这是接口重复使用的唯一时间.
我们系统中没有具体对象实现多个接口.
谁能告诉我这是否会导致问题在路上?如果是这样,他们会是什么?
我在想,我们的应用程序有很多重复,例如在这两个接口(编辑:在我们的服务层)中,唯一不同的是方法名称和它们所采用的参数类型,但在语义上他们做与他们发送消息的存储库相同:
interface ICustomer
{
void AddCustomer(Customer toAdd);
void UpdateCustomer(Customer toUpdate);
Customer GetById(int customerId);
}
interface IEmployee
{
void AddEmployee(Employee toBeAdded);
void UpdateEmployee(Employee toUpdate);
Employee GetById(int employeeId);
}
Run Code Online (Sandbox Code Playgroud)
这就是我认为重用的抽象原则会出现的地方,即将代码转换为类似的东西:
public interface IEmployee: IAdd<Employee>, IUpdate<Employee>, IFinder<Employee>
Run Code Online (Sandbox Code Playgroud)
这与存储库模式无关 - 这是关于任何层中的接口,看起来它们共享语义相同的行为.是否值得为这些操作派生通用接口并使"子接口"继承它们?
至少它会保持方法的签名一致.但这会给我带来什么其他好处呢?(除了利斯科夫替代原则)
现在,方法的名称和返回类型都到处都是.
我读过Mark Seemann关于Reused抽象原理的博客,但我不明白,坦白说.也许我只是愚蠢:)我还阅读了Fowler对Header Interfaces的定义.
Ayd*_*din 10
所有这一切都可以使用Repository pattern......
public interface IRepository<TEntity> where TEntity : IEntity
{
T FindById(string Id);
T Create(T t);
bool Update(T t);
bool Delete(T t);
}
public interface IEntity
{
string Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我们系统中没有具体对象实现多个接口.
谁能告诉我这是否会导致问题在路上?如果是这样,他们会是什么?
是的,如果还没有开始这样做会导致问题.
您最终会得到一堆接口,这些接口不会为您的解决方案增加任何内容,从而耗费大量时间来维护和创建它们.随着代码库的大小增加,您会发现并非所有内容都像您曾经想象的那样具有凝聚力
请记住,接口只是一种工具,是实现抽象级别的工具.而抽象是一个概念,一个模式,一个由许多独立实体共享的原型.
你总结了这个,
这与存储库模式无关 - 这是关于任何层中的接口,看起来它们共享语义相同的行为.是否值得为这些操作派生通用接口并使"子接口"继承它们?
这不是关于interfaces,这是关于abstractions,Repository pattern演示了如何抽象出针对特定对象定制的行为.
我上面给出的示例没有任何命名的方法AddEmployee或UpdateEmployee...这样的方法只是浅层接口,而不是抽象.
的概念Repository pattern是,它定义了一组由多个不同类的,每一个用于一个特定实体定制实施行为的明显.
考虑到为每个实体(UserRepository,BlogRepository等)实现了存储库,并且考虑到每个存储库必须支持一组核心功能(基本CRUD操作),我们可以采用该核心功能集并在接口中定义它,然后在每个存储库中实现该接口.
现在我们可以从Repository模式中学到一些东西,并将它应用到我们应用程序的其他部分,从而定义一个由新接口中的多个对象共享的核心行为集,然后从中派生出来.接口.
public interface IVehicleOperator<TVehicle> where TVehicle : IVehicle
{
void Accelerate();
void Brake();
}
Run Code Online (Sandbox Code Playgroud)
在这样做时,我们不再有1:1的映射,而是实际的抽象.
虽然我们讨论这个话题,但也值得一看decorator pattern.
Mar*_*ann 10
鉴于这种:
interface ICustomer{
void AddCustomer(Customer toAdd);
void UpdateCustomer(Customer toUpdate);
Customer GetById(int customerId);
}
interface IEmployee
{
void AddEmployee(Employee toBeAdded);
void UpdateEmployee(Employee toUpdate);
Employee GetById(int employeeId);
}
Run Code Online (Sandbox Code Playgroud)
我可能会首先重新设计它:
interface IRepository<T>
{
void Add(T toAdd);
void Update(T toUpdate);
T GetById(int id);
}
Run Code Online (Sandbox Code Playgroud)
但是,这可能仍然违反了接口隔离原则,更不用说因为它也违反了命令查询责任隔离 模式(不是架构),所以它也不能同时也不能成为逆变量.
因此,我的下一步可能是将它们拆分为角色接口:
interface IAdder<T>
{
void Add(T toAdd);
}
interface IUpdater<T>
{
void Update(T toAdd);
}
interface IReader<T>
{
T GetById(int id);
}
Run Code Online (Sandbox Code Playgroud)
此外,您可能会注意到IAdder<T>并且IUpdater<T>在结构上是相同的(它们仅在语义上不同),所以为什么不将它们设为一个:
interface ICommand<T>
{
void Execute(T item);
}
Run Code Online (Sandbox Code Playgroud)
为了保持一致,你也可以重命名IReader<T>:
interface IQuery<T>
{
T GetById(int id);
}
Run Code Online (Sandbox Code Playgroud)
从本质上讲,您可以减少这两个接口的所有内容,但对于某些人来说,这可能过于抽象,并且携带的语义信息太少.
但是,我认为不可能提供更好的答案,因为前提是有缺陷的.最初的问题是如何设计界面,但客户端无处可见.作为APPP ch.11告诉我们,"客户端[...]拥有抽象接口" - 换句话说,客户端根据需要定义接口.不应从具体类中提取接口.
关于这个主题的进一步研究材料