繁体   English   中英

公开接口而不是具体的类

[英]Exposing interfaces instead of concrete classes

我最近阅读了一些有关在公开集合而不是具体实现时使用接口的信息(IEnumerable而不是List)。 我现在正在尝试在我的代码中执行此操作。 但是,当我公开一个返回IEnumerable的属性时,我遇到了一些困难,即不允许将空值作为返回值。 例:

public class HumanResource
{
    public IEnumerable<EmployeeModel> Employees
    {
        get
        {
            // return what?
        }
    }
}

我应该在吸气剂中返回什么? 我不想为此使用自动属性,因为我想避免使用null。 我想要的是退回没有任何物品的新收藏。 当然,我可以返回实现IEnumerable的任何类型,但是该类的外部用户将如何知道呢? 还是我了解这个公开的界面,而不是错误的具体实现?

编辑:删除二传手

当然,我可以返回实现IEnumerable的任何类型,但是该类的外部用户将如何知道呢?

他们不必知道这一点,这就是重点。

您的媒体资源承诺会返回IEnumerable<EmplyeeModel> ,而这正是发生的情况。 您的代码返回哪个实现此接口的类都没有关系。

我想要的是退回没有任何物品的新收藏。

因此, Enumerable.Empty<EmplyeeModel>()new List<EmployeeModel>()都可以。


在设计API时,您需要考虑使用者将如何处理返回的数据类型,并据此做出决定。

通常,集合的IEnumerable<T>适合每个人。 当他们想要它在列表中时,他们可以执行new List<T>(yourEnumerable)yourEnumerable.ToArray()以将其用作数组。

我想要的是退回没有任何物品的新收藏。

属性使您可以很容易地做到这一点:

public class HumanResource
{
    // This is the real employees that gets returned when its not null
    private IEnumerable<EmployeeModel> employees; // may be null

    // This is the empty IEnumerable that gets returned when employees is null
    private static readonly IEnumerable<EmployeeModel> EmptyEmployees =
        new EmployeeModel[0];

    public IEnumerable<EmployeeModel> Employees
    {
        get
        {
            return employees ?? EmptyEmployees;
        }
        set {};
    }
}

employees变量设置为null时,代码将返回一个空数组。 您可以将employees设置为实现IEnumerable<EmployeeModel>的任何类型的集合,或者根据需要设置为数组。 这是可能的,因为您通过接口返回。

当然,另一方面,客户端将无法直接访问未通过接口公开的属性方法。 例如,如果employees实际上是一个List ,则调用者将不得不使用LINQ的Count()而不是直接获取.Count 当然,您可以公开另一个接口,例如IList<EmployeeModel> ,以使您的客户端使用其他方法。

您仍然需要为类中的属性提供内部支持集合。 您可以在构造函数中或在字段声明中初始化集合:

public class HumanResource
{
   private readonly IList<EmployeeModel> _employees = new List<EmployeeModel>();

   public IEnumerable<EmployeeModel> Employees
   {
     get
     {
         return _employees;
     }
     // No Setter - callers may only enumerate the collection
  }
}

顺便说一句,请注意,即使您确实使用了自动属性(例如List<EmployeeModel> ),它也会采用默认值null,除非在其他地方进行了初始化,所以这方面没有任何改变。

编辑,回复:有什么好处?

  • 通过移除设置器或将其设置为私有,我们可以防止调用者重新分配HumanResource的内部集合。
  • 通过将集合从List<> IEnumerable<>IEnumerable<> ,这意味着调用方只能对内部集合执行只读操作,例如对其进行迭代。 另外, IEnumerable<>可以用于延迟迭代中,从而允许调用方在拥有所需数据后立即退出枚举。
  • 根据下面的评论,如果调用者需要以其他集合(例如Array表示的数据,则可以使用LINQ扩展方法,例如.ToArray() .ToList().ToDictionary() 这样做将为调用者创建新的集合,但是会引用相同的EmployeeModel对象。 这样做对性能的影响很小。

最后一点要注意的是,通常没有必要将IEnumerable属性的setter设置为私有,或者将后备字段声明为IEnumerable ,因为这将防止类本身使用不纯的方法来操纵集合(即添加或删除对象) ),因为这样做需要强制转换,例如:

public class HumanResource
{
    public IEnumerable<EmployeeModel> Employees
    {
        get; 
        private set;
    }

    public HumanResource()
    {
        // Although the property can be assigned in the CTor to prevent the null issue ...
        Employees = new List<EmployeeModel>();
    }

    private void AddProductiveEmployee()
    {
        // ... We need to continually cast within the class, which is just silly.
        (Employees as IList).Add(new EmployeeModel());
    }

使用内部IEnumerable<>的手动支持字段方法会遇到相同的问题

// Unlikely to be useful
private readonly IEnumerable<EmployeeModel> _employees = new List<EmployeeModel>();

TL; DR

  • 使用适合于类内部使用的集合类型(OO组成)
  • 但是在外部接口(例如公共获取器/属性)上,将内部实现隐藏到最小(OO封装)
  • 在构造函数(或内联)中初始化内部集合将防止暴露空集合

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM