亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#中實現Fluent Interface的三種方法

2020-01-24 02:04:07
字體:
來源:轉載
供稿:網友

背景知識

Fluent Interface是一種通過連續的方法調用以完成特定邏輯處理的API實現方式,在代碼中引入Fluent Interface不僅能夠提高開發效率,而且在提高代碼可讀性上也有很大的幫助。從C# 3.0開始,隨著擴展方法的引入,Fluent Interface也更多地被開發人員熟悉和使用。例如,當我們希望從一個整數列表中找出所有的偶數,并將這些偶數通過降序排列的方式添加到另一個列表中時,可以使用下面的代碼:

復制代碼 代碼如下:

i.Where(p => p % 2 == 0)
    .OrderByDescending(q => q)
    .ToList()
    .ForEach(r => result.Add(r));

這段代碼不僅看起來非常清晰,而且在編寫的時候也更符合人腦的思維方式,通過這些連續的方法調用,我們首先從列表i中尋找所有的偶數,然后對這些偶數進行排序并將排序后的值逐個添加到result列表中。

在實際應用中,Fluent Interface不僅僅是使用在類似上面的查詢邏輯上,而它更多地是被應用開發框架的配置功能所使用,比如在Entity Framework Code First中可以使用Fluent API對實體(Entity)和模型(Model)進行配置,此外還有流行的ORM框架NHibernate以及企業服務總線框架NServiceBus等等,都提供了類似的Fluent API,以簡化框架的配置過程。這些API都是Fluent Interface的具體實現。由于Fluent Interface的方法鏈中各方法的名稱都具有很強的描述性,而且具有單一職責的特點,所以Fluent Interface也可以看成是完成某一領域特定任務的“領域特定語言(Domain Specific Language)”,比如在上面的例子中,Fluent Interface被用于查詢領域,而在Entity Framework、NHiberante和NServiceBus等框架中,它又被用于框架的配置領域。

接下來,讓我們首先看一下Fluent Interface的簡單實現方式,并簡要地討論一下這種實現方式的優缺點,再來了解一下一種使用裝飾器(Decorator)模式和擴展接口的實現方式。

Fluent Interface的簡單實現

Fluent Interface的一種簡單實現就是在類型的每個方法中對傳入參數進行處理,然后返回該類型本身的實例,因此,當該類型的某個方法被調用后,進而還可以連續地直接調用其它的方法而無需在調用時指定該類型的實例。現假設我們需要實現某個服務接口IService,在這個接口中,要用到一個提供緩存功能的接口ICache以及一個提供日志記錄的接口ILogger,為了讓IService的實例能夠以Fluent Interface的方式指定自己所需要的ICache接口和ILogger接口的實例,我們可以這樣定義IService接口:

復制代碼 代碼如下:

public interface IService
{
    ICache Cache { get; }
    ILogger Logger { get; }
    IService UseCache(ICache cache); // return ‘this' in implemented classes
    IService UseLogger(ILogger logger); // return ‘this' in implemented classes
}

于是,對IService實例的配置就變得非常簡單,比如:

復制代碼 代碼如下:

IService aService = new Service();
aService.UseCache(new AppfabricCache()).UseLogger(new ConsoleLogger());

這是最簡單的Fluent Interface的實現方式,對于一些簡單的應用場景,使用這種簡單快捷的方式的確是個不錯的選擇,但在體驗著這種便捷的同時,我們或許還需要進行更進一步的思考:

1.直接定義在IService接口上的UseCache和UseLogger方法會破壞IService本身的單一職責性,而這又是與軟件設計的思想是沖突的。到底是用哪種緩存服務和哪種日志服務,這并不是IService需要考慮的問題。當然,C#的擴展方法可以很方便地把UseCache和UseLogger等方法從IService接口中剝離出去,但更合理的做法是,使用工廠來創建IService的實例,而創建實例的依據(上下文)則應該由其它的配置信息來源提供
2.無法保證上下文的正確性。在上面的例子中,這個問題并不明顯,先調用UseCache還是先調用UseLogger并不會給結果造成任何影響。但在某些應用場景中,設置的對象之間本身就存在一定的依賴關系,比如在Entity Framework Code First的Entity Type Configuration中,只有當所配置的屬性是字符串的前提下,才能夠進一步對該屬性的最大長度、是否是Unicode等選項進行設置,否則Fluent Interface將不會提供類似的方法調用。顯然目前這個簡單的實現并不能滿足這種需求
3.需要首先創建IService類型的實例,然后才能使用UseCache和UseLogger等方法對其進行設置,如果在實例的創建過程中存在對ICache或者ILogger的依賴的話(比如在構造函數中希望能夠使用ILogger的實例寫一些日志信息等),那么實現起來就會比較困難了

鑒于以上三點分析,當需要在應用程序或開發框架中更為合理地引入Fluent Interface時,上述簡單的實現方式就無法滿足所有需求了。為此,我采用裝飾器模式,并結合C#的擴展方法特性來實現Fluent Interface,這種方式不僅能夠解決上面的三種問題,而且面向對象的設計會使Fluent Interface的擴展變得更加簡單。

使用裝飾器模式和擴展方法實現Fluent Interface

仍然以上文中的IService接口為例,通過分析我們可以得到兩個啟示:首先,對于IService的實例究竟應該是采用哪種緩存機制以及哪種日志記錄機制,這就是一種對IService的實例進行配置的過程;其次,這種配置過程就相當于在每個配置階段逐漸地向已有的配置信息上添加新的信息,比如最開始創建一個空的配置信息,在第一階段確定了所選用的緩存機制時,就會在這個空的配置信息基礎上添加與緩存相關的配置信息,而在第二階段確定了所選用的日志記錄機制時,又會在前一階段獲得的配置信息基礎上再添加與日志記錄相關的配置信息,這個過程正好是裝飾器模式的一種應用場景。最后一步就非常簡單了,程序只需要根據最終得到的配置信息初始化IService接口的實例即可。為了簡化實現過程,我選擇Microsoft Patterns & Practices Unity Application Block的IoC容器來實現這個配置信息的管理機制。選用Unity IoC容器的好處是,對接口及其實現類型的注冊并沒有先后順序的要求,IoC容器會自動分析類型之間的依賴關系并對類型進行注冊。事實上在很多應用程序開發框架中,也是用這種方式在框架的配置部分實現Fluent Interface的。

裝飾器模式的引入

首先我們引入“配置器”的概念,配置器的作用就是對IService實例初始化過程中的某個方面(例如緩存或者日志)進行配置,它會向調用者返回一個Unity IoC容器的實例,以便調用方能夠在該配置的基礎上進行其它方面的配置操作(為了簡化起見,下文中所描述的“配置”僅表示選擇某種特定類型的實現,而不包含其它額外的配置內容)。我們可以使用如下接口對配置器進行定義:

復制代碼 代碼如下:

public interface IConfigurator
{
    IUnityContainer Configure();
}

為了實現的方便,我們還將引入一個抽象類,該抽象類實現了IConfigurator接口,并將其中的Configure方法標識為抽象方法。于是,對于任何一種配置器而言,它只需要繼承于該抽象類,并且重載Configure方法即可實現配置邏輯。該抽象類的定義如下:

復制代碼 代碼如下:

public abstract class Configurator : IConfigurator
{
    readonly IConfigurator context;
 
    public Configurator(IConfigurator context)
    {
        this.context = context;
    }
 
    protected IConfigurator Context
    {
        get
        {
            return this.context;
        }
    }
 
    public abstract IUnityContainer Configure();
}

接下來就是針對不同的配置環節實現各自的配置器了。我們以緩存機制的配置為例,簡要介紹一下“緩存配置器”的實現方式。

先定義一個名為ICacheConfigurator的接口,該接口實現了IConfigurator的接口,但它是一個空接口,并不包含任何屬性、事件或方法的接口定義。引入這個接口的目的就是要在接下來的擴展方法定義中能夠實現面向該接口的方法擴展,于是上文中討論的第二個問題就能引刃而解,這將在接下來的“擴展方法的引入”部分進行討論。事實上在很多成熟的應用程序和框架中也有類似的設計,比如將接口用作泛型約束類型等。因此,ICacheConfigurator的實現代碼非常簡單:

復制代碼 代碼如下:

public interface ICacheConfigurator : IConfigurator
{
}

而作為“緩存配置器”而言,它只需要繼承于Configurator類并實現ICacheConfigurator接口就可以了,代碼如下:

復制代碼 代碼如下:

public class CacheConfigurator<TCache> : Configurator,
    ICacheConfigurator
    where TCache : ICache
{
 
    public CacheConfigurator(IConfigurator configurator)
        : base(configurator)
    {
    }
 
    public override IUnityContainer Configure()
    {
        var container = this.Context.Configure();
        container.RegisterType<ICache, TCache>();
        return container;
    }
}

從上面的代碼中可以看到,TCache約束于ICache接口類型,而在Configure方法中,首先調用配置上下文(也就是配置器本身所包含的上一層配置器實例)的Configure方法,同時獲得已配置的Unity IoC容器實例container,之后在container上繼續調用RegisterType方法,將給定的緩存機制實現類型注冊到container中,最后將container返回給調用者。

整個配置器部分的實現,可以用下面的類圖進行總結:

擴展方法的引入

前面已經提到過,擴展方法可以將職責無關的方法定義從類型中移出,并在一個靜態類中進行集中實現。在目前的這個例子中,擴展方法還能夠幫助我們將類型繼承的層次結構“扁平化”,使得Fluent Interface中各方法的銜接邏輯變得更加清晰。仍然以緩存配置部分為例,假設我們希望在獲得了服務的配置之后,能夠接著對緩存機制進行配置,在完成了緩存機制的配置后,才能開始對日志記錄機制進行配置,那么我們就可以定義擴展方法如下:

復制代碼 代碼如下:

public static ICacheConfigurator WithDictionaryCache(this IServiceConfigurator configurator)
{
    return new CacheConfigurator<DictionaryCache>(configurator);
}
public static ILoggerConfigurator WithConsoleLogger(this ICacheConfigurator configurator)
{
    return new LoggerConfigurator<ConsoleLogger>(configurator);
}

上面的WithDictionaryCache方法表示需要在Service的配置上采用基于字典的緩存機制,而WithConsoleLogger則表示在緩存配置的基礎上,還需要選用控制臺作為日志記錄機制。

從上面的代碼中我們還能了解到,擴展方法還能夠很直觀地定義各種配置之間的先后順序,更改起來也非常方便。例如,如果緩存機制和日志記錄機制的配置沒有一個前后關系的話,那么我們可以將IServiceConfigurator作為WithConsoleLogger的第一個參數類型,而無需去修改代碼中的其它任何部分。

接下來要做的,就是設計一個工廠類,使其能夠根據我們的配置信息創建一個新的IService實例。

工廠類的實現

工廠類的實現就非常簡單了,同樣使用擴展方法,對IConfigurator類型進行擴展,在獲得了Unity IoC容器的實例之后,只需要調用Resolve方法直接返回IService類型的實現類型就可以了。Resolve方法的使用,直接解決了上文中提到的第三個問題。工廠類的代碼如下:

復制代碼 代碼如下:

public static class ServiceFactory
{
    public static IToConfigConfigurator ToConfig()
    {
        return new ToConfigConfigurator();
    }
 
    public static IService Create()
    {
        return ToConfig().Service().Create();
    }
 
    public static IService Create(this IConfigurator configurator)
    {
        var container = configurator.Configure();
        if (!container.IsRegistered<ICache>())
            container.RegisterType<ICache, DictionaryCache>();
        if (!container.IsRegistered<ILogger>())
            container.RegisterType<ILogger, ConsoleLogger>();
        if (!container.IsRegistered<IService>())
            container.RegisterType<IService, Service>();
        return container.Resolve<IService>();
    }
}

測試

創建一個測試項目以便對我們所做的工作進行測試,比如下面的測試方法將會對IService的實現所采用的緩存機制類型和日志記錄機制類型進行測試:

復制代碼 代碼如下:

[TestMethod]
public void UseAppfabricCacheAndDatabaseLoggerTest()
{
    var service = ServiceFactory
        .ToConfig()
        .Service()
        .WithAppfabricCache()
        .WithDatabaseLogger()
        .Create();
    Assert.IsInstanceOfType(service.Cache, typeof(AppfabricCache));
    Assert.IsInstanceOfType(service.Logger, typeof(DatabaseLogger));
}

現在我們已經可以使用Fluent Interface對IService實例的初始化過程進行配置了。Fluent Interface的引入,更像是在使用一種自然語言對配置過程進行表述:Service factory, to config (the) service with Appfabric Cache (mechanism) (and) with Database Logger (mechanism)。

總結

本文首先介紹了Fluent Interface的相關知識,并給出了一種簡單的實現方式。通過對簡單實現方式的討論,引出了可能存在的設計問題,進而選擇了一種更為合理的實現方式,即通過使用裝飾器模式和C#的擴展方法特性來實現Fluent Interface。這種全新的實現方式不僅能夠解決所討論的設計問題,而且這種面向對象的設計方式還為Fluent Interface的實現帶來了一定的可擴展性。文章最后對這種實現方式進行了簡單測試,同時也展示了Fluent Interface在實際中的應用。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲一区二区久久久久久久| 久久久久久中文字幕| 亚洲成人av资源网| 欧美一级电影免费在线观看| 美日韩精品免费观看视频| 欧美日韩性视频在线| 最近中文字幕mv在线一区二区三区四区| 在线视频一区二区| 欧美一区二粉嫩精品国产一线天| 久久国产视频网站| 欧美激情喷水视频| 日本国产一区二区三区| 欧美性视频网站| 亚洲精品在线观看www| 91探花福利精品国产自产在线| 欧美高清视频一区二区| 亚洲人成电影网站色xx| 欧美一区二区三区艳史| 尤物yw午夜国产精品视频明星| 欧美精品亚州精品| 成人在线精品视频| 97在线免费观看| 欧美一级淫片aaaaaaa视频| 国产精品露脸av在线| 日韩欧美在线中文字幕| 亚洲区免费影片| 91成人福利在线| 97色伦亚洲国产| zzjj国产精品一区二区| 久久久久亚洲精品成人网小说| 亚洲福利精品在线| 一区二区中文字幕| 不卡在线观看电视剧完整版| 在线a欧美视频| 久久露脸国产精品| 国产精品激情av电影在线观看| 国产精品久久久久久久久免费看| 国产中文欧美精品| 国产精品成久久久久三级| 日韩av在线一区二区| 亚洲国模精品一区| 欧美日韩国产精品| 欧美激情欧美狂野欧美精品| 成人激情电影一区二区| 精品久久久一区| 欧美老女人在线视频| 91干在线观看| 欧美激情一区二区三级高清视频| 最近2019中文字幕mv免费看| 色综合久久久久久中文网| 国产精品成人在线| 一本色道久久88综合亚洲精品ⅰ| 日韩一区视频在线| 亚洲乱码国产乱码精品精天堂| 国产精品三级久久久久久电影| 国产盗摄xxxx视频xxx69| 美女国内精品自产拍在线播放| 国产欧美日韩中文| 国产午夜精品视频免费不卡69堂| 欧美美女15p| 国产成人一区三区| 日韩欧美中文第一页| 91视频国产高清| 国产欧美一区二区三区在线| 欧美成人免费一级人片100| 日韩av色综合| 亚洲女人被黑人巨大进入| 国产精品18久久久久久首页狼| 亚洲一二在线观看| 亚洲www永久成人夜色| 久久综合伊人77777蜜臀| 在线观看成人黄色| 欧美大成色www永久网站婷| 久久99久国产精品黄毛片入口| 国产精品成人aaaaa网站| 国产成人精品国内自产拍免费看| 久久九九国产精品怡红院| 亚洲色图日韩av| 久久精品国产亚洲7777| 色噜噜狠狠狠综合曰曰曰| www.国产精品一二区| 国产精品白嫩美女在线观看| 亚洲欧美福利视频| 欧美激情精品久久久久久黑人| xvideos成人免费中文版| 久久视频在线观看免费| 国产一区二区三区在线视频| 亚洲a在线观看| 最好看的2019的中文字幕视频| 欧美电影免费观看高清完整| 在线观看免费高清视频97| 中文字幕在线看视频国产欧美在线看完整| 中文字幕av日韩| 中文在线资源观看视频网站免费不卡| 亚洲理论电影网| 久久久久久成人精品| 欧美一级片久久久久久久| 精品国产一区二区三区四区在线观看| 日韩电影免费在线观看中文字幕| 欧美成aaa人片在线观看蜜臀| 久久久久久久久久av| 北条麻妃一区二区在线观看| 精品久久久久久亚洲精品| 欧美性受xxxx黑人猛交| 久久久久久91香蕉国产| 久久精品国产v日韩v亚洲| 色综合色综合网色综合| 91精品久久久久久久久久久久久久| 亚洲国产精彩中文乱码av在线播放| 亚洲人精选亚洲人成在线| 日韩一区二区欧美| 国产精品人成电影| 国产精品一区二区久久久| 自拍偷拍亚洲区| 日韩在线观看免费高清| 97超碰国产精品女人人人爽| 日韩中文字幕在线观看| 亚洲人成77777在线观看网| 色先锋久久影院av| 国产91精品不卡视频| 亚洲午夜av电影| 久久这里只有精品99| 国产日韩在线看片| 国产成人av网| 成人精品福利视频| 91久久久久久久久久久| 一区二区三区www| 日韩免费视频在线观看| 亚洲va欧美va国产综合剧情| 日韩美女在线看| 国产精品美女在线观看| 91精品啪在线观看麻豆免费| 久久精品亚洲热| 亚洲男人天堂古典| 亚洲精品ady| 亚洲人av在线影院| 久久资源免费视频| 日韩av免费看网站| 国产亚洲美女精品久久久| 亚洲欧美一区二区三区情侣bbw| 国产一区av在线| 日韩高清电影免费观看完整版| 日韩欧美在线免费| 国产精品久久久久久久久久久新郎| 国产日本欧美视频| 精品视频久久久久久久| 亚洲九九九在线观看| 国产日韩欧美中文| 成人h视频在线观看播放| 欧美精品情趣视频| 久久久久国产精品免费网站| www.久久撸.com| 中文字幕视频一区二区在线有码| 国产精品视频yy9099| 亚洲欧美精品中文字幕在线| 欧美性猛交xxxx乱大交| 欧美尤物巨大精品爽| 久久久www成人免费精品| 久久久国产精彩视频美女艺术照福利| 亚洲精品久久久久中文字幕欢迎你| 亚洲人成在线观| 欧美丰满片xxx777| 国产区亚洲区欧美区| 亚洲伊人久久综合|