簡體   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