【问题标题】:Interface as one used for inheritance reasons, not implementors接口作为一种用于继承的原因,而不是实现者
【发布时间】:2019-11-28 13:36:45
【问题描述】:

.NET中的接口实际上是编译成类,纯抽象类,区别只是在其上加上interface前缀。所以一般来说,任何接口的实现者都是该接口的继承者,而不是许多书中写的实现者。是的,我知道接口在企业代码中还有另一个目的——为不相关的类提供通用功能。但是对于 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


【解决方案1】:

您描述的两种技术都是有效的,但是您提出的技术可能会使用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 方法。在这种情况下,您可以让实体通过实现额外的接口来描述它们的功能。

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

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

如果您想深入了解,请阅读Role InterfacesInterface Segregation PrincipleSOLID 中的“我”)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-20
    • 2023-03-13
    • 2011-10-23
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    相关资源
    最近更新 更多