首页 > 解决方案 > 接口作为一种用于继承的原因,而不是实现者

问题描述

.NET中的接口实际上是编译成类,纯抽象类,只是有接口的区别前缀。所以一般来说,任何接口的实现者都是该接口的继承者,而不是许多书中写的实现者。是的,我知道接口在企业代码中还有另一个用途——为不相关的类提供通用功能。但是对于 MSIL,任何类实际上都是接口的子类型。它给出了一个理由只用简单的具体类扩展接口,而不是创建基接口的另一个派生接口。因此,当某些客户端引用了该接口并且需要向下转换为更具体的实体时,开发人员会创建另一个派生接口并向下转换为它。为什么他们不直接向下转换为具体派生类,因为它实际上是接口的有效子类型?示例代码:

public inteface IEntity
{
     int Id { get; set; } 
}

public class Vote : IEntity 
{
     public int Id { get; set; }

     public int SubjectId { get; set; }

     public bool Sign { get; set;}

     public int UserId {get; set;} 

     public void Validate() 
     {
         if (UserId == default)
            throw new Exception("UserId is not defined");
         if (SubjectId == default)
            throw new Exception("SubjectId is not defined");
     }
}

public abstract class BusinessEngine
{
     private readonly IRepository _repo;

      public void Save(IEntity entity) 
      {
          BeforeSave(entity);
          DoSave(entity); // omit code, it just delegates work to repository
          AfterSave();
      }

      protected virtual void BeforeSave(IEntity entity) 
      {
      }

      protected virtual void AfterSave(IEntity entity) 
      {
      }
} 

public class VotesBE: BusinessEngine
{
      protected override void BeforeSave(IEntity entity) 
      {
            var vote = entity as Vote;
            vote.Validate();
      }
}

但是许多开发人员更愿意制作另一个派生接口IEntity——比如说,IValidatableEntity扩展基本IEntity接口,他们会在 BusinessEngine 的代码中做这件事:

`public abstract class BusinessEngine
{
     private readonly IRepository _repo;

      public void Save(IEntity entity) 
      {
          BeforeSave(entity);
          DoSave(entity); // omit code, it just delegates work to repository
          AfterSave();
      }

      protected virtual void BeforeSave(IEntity entity) 
      {
        if (entity is IValidatableEntity ve)
              ve.Validate();
      }

      protected virtual void AfterSave(IEntity entity) 
      {
      }
}`

标签: c#asp.net.net.net-core

解决方案


您描述的两种技术都是有效的,但是您提出的技术可能会使用Generics实现。

例如:

public abstract class BusinessEngine<TEntity> where TEntity : IEntity
{
     private readonly IRepository _repo;

      public void Save(TEntity entity) 
      {
          var typedEntity = entity as TEntity;

          BeforeSave(entity);
          DoSave(entity);
          AfterSave();
      }

      protected virtual void BeforeSave(TEntity entity) { }

      protected virtual void AfterSave(TEntity entity) { }
}
public class VoteBusinessEngine : BusinessEngine<Vote>
{
      protected override void BeforeSave(Vote entity) 
      {
            vote.Validate();
      }
}

现在,对于您最初的问题“为什么以一种方式而不是另一种方式?” 这取决于您的目标和需求。

如果您的目标是编写可以处理多种类型实体的通用服务(或服务集合),您可能需要这种IValidatableEntity方法。在这种情况下,您让实体通过实现额外的接口来描述它们的功能。

如果您想在不修改实体本身的情况下为单个服务添加几个实体类型的特殊情况处理,那么通用方法是有意义的。

这真的归结为:你想“拥有”谁的特殊行为?实体还是服务?

如果您想深入了解,请阅读角色接口接口隔离原则( SOLID中的“I” )。


推荐阅读