C#接口顯示實現在實際開發中的作用


摘要

任何一個C#入門的程序員都知道——當一個類型在實現接口的時候,有兩種方法實現:顯式實現、隱式實現。而且大家也都知道,當一個類型實現的兩個接口存在相同成員定義時,顯示實現可以解決這種情況。

 

但是,在一個命名比較規范的項目中,幾乎不可能出現上述情況。

 

那么,顯示實現有什么具體存在的意義嗎?

本人根據這小幾年的開發歷經,感覺顯式實現最覺的兩個作用就是:

  1. 改變接口成員的使用權限
  2. 改變接口成員的出入參數

下面本人將會對這兩種作用進一一說明


接口定義

本篇文章將會使用一個示例來講述這兩個作用。

我們先來看一下示例吧:

  1. 定義一個接口類型,表示一個"元件",元件擁有兩個成員
    1. 元件名稱,主要是用來和其它元件進行區分
    2. 使用元件,需要一個Object類型的參數,表示將元件使用在哪個目標對象上
  2. 定義一個接口類型,表示一個"元件工廠",用來生成"元件"的實例,元件工廠擁有兩個成員
    1. 根據指定的元件名稱,生成一個元件實例
    2. 注冊一個元件,只有被注冊過的元件才能夠被生產

 

例子還是挺簡單的,我們來把這些接口類型碼出來吧:

/// <summary>
/// 一個元件接口
/// </summary>
interface IComponent
{
    /// <summary>
    /// 元件的名稱,用於區別於其它元件
    /// </summary>
    String ComponentName { get; }

    /// <summary>
    /// 使用這個元件
    /// <param name="target">對哪個對象使用這個元件</param>
    /// </summary>
    void Use(Object target);
}

/// <summary>
/// 一個元件工廠接口
/// </summary>
interface IComponentFactory
{
    /// <summary>
    /// 創建元件
    /// </summary>
    /// <param name="componentName"></param>
    /// <returns></returns>
    IComponent CreateComponent(String componentName);

    /// <summary>
    /// 注冊一個元件
    /// </summary>
    /// <param name="component"></param>
    void RegistComponent(IComponent component);
}

 


 

 

實現鎖與鑰匙的關系

光有接口,我們是無法工作的。因此我們開發一個叫鑰匙的元件,再開發一個密碼鎖,鑰匙可以用來開密碼鎖,當兩者的編號相同時,鎖才可以被打開。我們再做一個工廠,根據鎖的編號生成一把鑰匙,然后就可以開鎖了。

我們先用隱式實現來做這件事

/// <summary>
/// 密碼鎖
/// </summary>
class PasswordLocker
{
    /// <summary>
    /// 開鎖用的密碼
    /// </summary>
    public String Code { get; set; }
}

/// <summary>
/// 鑰匙
/// </summary>
class Key : IComponent
{
    /// <summary>
    /// 對應的解鎖密碼,只有和鎖的Code相同時才可以開鎖
    /// </summary>
    public string Code { get; set; }

    public string ComponentName
    {
        get { return this.Code; }
    }

    public void Use(object target)
    {
        //由於入參是繼承了接口的Object類型,所以必須先對入參進行類型判斷
        if (target is PasswordLocker)
        {
            PasswordLocker pl = (PasswordLocker)target;
            if (pl.Code == this.Code) Console.WriteLine("打開鎖了");
            else Console.WriteLine("未能把鎖打開");
        }
        else
            Console.WriteLine("目前類型不是鎖,暫時不能使用該元件");

    }
}

/// <summary>
/// 鑰匙工廠
/// </summary>
class KeyFactory : IComponentFactory
{
    private Dictionary<String, IComponent> components = new Dictionary<string, IComponent>();

    public IComponent CreateComponent(string componentName)
    {
        return components[componentName];
    }

    /// <summary>
    /// 由外部創建一個元件
    /// </summary>
    /// <param name="component"></param>
    public void RegistComponent(IComponent component)
    {
        components[component.ComponentName] = component;
    }
}

然后我們再寫一代段碼使用他們:

PasswordLocker locker = new PasswordLocker();
locker.Code = "12345";

Key k = new Key();
k.Code = "12345";
KeyFactory factory = new KeyFactory();
factory.RegistComponent(k);
factory.CreateComponent(locker.Code).Use(locker);

功能全部OK,但是有一些小小的瑕疵:

  1. 這把鑰匙從代碼上看,還可以用在不是PasswordLocker的類型上,所以Use方法中還要對入參類型進行判斷;
  2. 明明是鑰匙工廠,但從代碼上看生產出來的依然只是元件接口類型,注冊的時候也是,希能夠直接使用鑰匙類型;
  3. 如果我的元件工廠是能夠自動在實例化的時候,從數據庫里注冊好所有元件了,那我自然不希望調用工廠的人可以繼續Regist元件。

確實如此,當一個項目大到多人合作的時候,能夠有着明確的入參與返回類型私有化不需要其它人員調用的成員,是很有必要的一件事。

所以現在回頭看剛才的那段實現代碼,我們可以提出以下要求:

  1. 鑰匙只能對鎖進行Use
  2. 工廠從數據庫中初始化所有鑰匙,然后不再開放Regist方法
  3. 工廠生成出來的元件就是鑰匙,而不是其它類型。

 

對新要求的實現:

我們通過對接口的顯示實現,以保證接口成員不可以被顯示的調用。再定義需要的public方法,調用接口成員,以達到上面三個要求:

/// <summary>
/// 這是一個僅僅能用於開密碼鎖的元件,使用了現式實現改變入參
/// </summary>
class PasswordLockerKey : IComponent
{
    private string code;

    public PasswordLockerKey(String code)
    {
        this.code = code;
    }

    string IComponent.ComponentName
    {
        get { return this.code; }
    }

    void IComponent.Use(object target)
    {
        PasswordLocker pl = (PasswordLocker)target;
        if (pl.Code == this.code) Console.WriteLine("打開鎖了");
        else Console.WriteLine("未能把鎖打開");
    }

    /// <summary>
    /// 利用顯示繼承,隱藏了以Object作為入參的方法,並開放了一個僅僅以PasswordLocker作為入參的方法。改變了參數類型
    /// </summary>
    /// <param name="locker"></param>
    public void User(PasswordLocker locker)
    {
        //將自身轉化為接口類型,再調用Use才可以使用顯式實現的方法
        ((IComponent)this).Use(locker);
    }
}

/// <summary>
/// 基於數據庫的鑰匙工廠,使用顯式實現改變了接口成員的訪問權限
/// </summary>
class DataBaseKeyFactory : IComponentFactory
{
    private Dictionary<String, PasswordLockerKey> keys = new Dictionary<string, PasswordLockerKey>();

    /// <summary>
    /// 在構造函數的同時,從數據庫中加載出所有鑰匙
    /// </summary>
    public DataBaseKeyFactory()
    {
        IComponentFactory f = (IComponentFactory)this;
        foreach (PasswordLockerKey k in LoadKeyFromDatabase())
        {
            f.RegistComponent(k);
        }
    }

    /// <summary>
    /// 這是模擬的通過數據庫加載鑰匙的方法
    /// </summary>
    /// <returns></returns>
    protected virtual IEnumerable<PasswordLockerKey> LoadKeyFromDatabase()
    {
        return new List<PasswordLockerKey>() 
        { 
            new PasswordLockerKey("12345")
        };
    }

    IComponent IComponentFactory.CreateComponent(string componentName)
    {
        return keys[componentName];
    }

    void IComponentFactory.RegistComponent(IComponent component)
    {
        keys[component.ComponentName] = (PasswordLockerKey)component;
    }

    /// <summary>
    /// 這里改變了原本接口的返回類型
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    public PasswordLockerKey CreateComponent(string code)
    {
        return (PasswordLockerKey)((IComponentFactory)this).CreateComponent(code);
    }
}

代碼中模擬了一下從數據庫中取數的過程。

通過上面的方法,我們最終在使用這些類形的時候會發現,Use的入參對象只能是PasswordLocker,Factory不可以Regist成員了,Create時一定是PasswordLockerKey類型。已經基本滿足上面的需要了。

 


小結

在實際項目中,其實這些現象依然是很少見的。往往出現在底層接口框架被搭建好后,對接口類型進行一次基礎實現時,可能會遇到這些問題。

本人寫此文章,希望大家在遇到類似問題的時候,能夠想到可以使用接口的顯示實現來解決。

希望能夠大家帶來一些提示和幫助。

 

 

 文章為作者原創,轉載請注明出處http://www.cnblogs.com/ShimizuShiori/p/5468749.html ,謝謝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM