成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

在C#中如何使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)FluentInterface

本篇文章為大家展示了在C#中如何使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)Fluent Interface,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

相山網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)建站自2013年創(chuàng)立以來(lái)到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站。

在C#中使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)Fluent Interface

背景知識(shí)

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

1

2

3

4

i.Where(p => p % 2 == 0)

    .OrderByDescending(q => q)

    .ToList()

    .ForEach(r => result.Add(r));

這段代碼不僅看起來(lái)非常清晰,而且在編寫(xiě)的時(shí)候也更符合人腦的思維方式,通過(guò)這些連續(xù)的方法調(diào)用,我們首先從列表i中尋找所有的偶數(shù),然后對(duì)這些偶數(shù)進(jìn)行排序并將排序后的值逐個(gè)添加到result列表中。

在實(shí)際應(yīng)用中,F(xiàn)luent Interface不僅僅是使用在類似上面的查詢邏輯上,而它更多地是被應(yīng)用開(kāi)發(fā)框架的配置功能所使用,比如在Entity Framework Code First中可以使用Fluent API對(duì)實(shí)體(Entity)和模型(Model)進(jìn)行配置,此外還有流行的ORM框架NHibernate以及企業(yè)服務(wù)總線框架NServiceBus等等,都提供了類似的Fluent API,以簡(jiǎn)化框架的配置過(guò)程。這些API都是Fluent Interface的具體實(shí)現(xiàn)。由于Fluent Interface的方法鏈中各方法的名稱都具有很強(qiáng)的描述性,而且具有單一職責(zé)的特點(diǎn),所以Fluent Interface也可以看成是完成某一領(lǐng)域特定任務(wù)的“領(lǐng)域特定語(yǔ)言(Domain Specific Language)”,比如在上面的例子中,F(xiàn)luent Interface被用于查詢領(lǐng)域,而在Entity Framework、NHiberante和NServiceBus等框架中,它又被用于框架的配置領(lǐng)域。

接下來(lái),讓我們首先看一下Fluent Interface的簡(jiǎn)單實(shí)現(xiàn)方式,并簡(jiǎn)要地討論一下這種實(shí)現(xiàn)方式的優(yōu)缺點(diǎn),再來(lái)了解一下一種使用裝飾器(Decorator)模式和擴(kuò)展接口的實(shí)現(xiàn)方式。

Fluent Interface的簡(jiǎn)單實(shí)現(xiàn)

Fluent Interface的一種簡(jiǎn)單實(shí)現(xiàn)就是在類型的每個(gè)方法中對(duì)傳入?yún)?shù)進(jìn)行處理,然后返回該類型本身的實(shí)例,因此,當(dāng)該類型的某個(gè)方法被調(diào)用后,進(jìn)而還可以連續(xù)地直接調(diào)用其它的方法而無(wú)需在調(diào)用時(shí)指定該類型的實(shí)例?,F(xiàn)假設(shè)我們需要實(shí)現(xiàn)某個(gè)服務(wù)接口IService,在這個(gè)接口中,要用到一個(gè)提供緩存功能的接口ICache以及一個(gè)提供日志記錄的接口ILogger,為了讓IService的實(shí)例能夠以Fluent Interface的方式指定自己所需要的ICache接口和ILogger接口的實(shí)例,我們可以這樣定義IService接口:

1

2

3

4

5

6

7

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

}

于是,對(duì)IService實(shí)例的配置就變得非常簡(jiǎn)單,比如:

1

2

IService aService = new Service();

aService.UseCache(new AppfabricCache()).UseLogger(new ConsoleLogger());

這是最簡(jiǎn)單的Fluent Interface的實(shí)現(xiàn)方式,對(duì)于一些簡(jiǎn)單的應(yīng)用場(chǎng)景,使用這種簡(jiǎn)單快捷的方式的確是個(gè)不錯(cuò)的選擇,但在體驗(yàn)著這種便捷的同時(shí),我們或許還需要進(jìn)行更進(jìn)一步的思考:

  1. 直接定義在IService接口上的UseCache和UseLogger方法會(huì)破壞IService本身的單一職責(zé)性,而這又是與軟件設(shè)計(jì)的思想是沖突的。到底是用哪種緩存服務(wù)和哪種日志服務(wù),這并不是IService需要考慮的問(wèn)題。當(dāng)然,C#的擴(kuò)展方法可以很方便地把UseCache和UseLogger等方法從IService接口中剝離出去,但更合理的做法是,使用工廠來(lái)創(chuàng)建IService的實(shí)例,而創(chuàng)建實(shí)例的依據(jù)(上下文)則應(yīng)該由其它的配置信息來(lái)源提供

  2. 無(wú)法保證上下文的正確性。在上面的例子中,這個(gè)問(wèn)題并不明顯,先調(diào)用UseCache還是先調(diào)用UseLogger并不會(huì)給結(jié)果造成任何影響。但在某些應(yīng)用場(chǎng)景中,設(shè)置的對(duì)象之間本身就存在一定的依賴關(guān)系,比如在Entity Framework Code First的Entity Type Configuration中,只有當(dāng)所配置的屬性是字符串的前提下,才能夠進(jìn)一步對(duì)該屬性的最大長(zhǎng)度、是否是Unicode等選項(xiàng)進(jìn)行設(shè)置,否則Fluent Interface將不會(huì)提供類似的方法調(diào)用。顯然目前這個(gè)簡(jiǎn)單的實(shí)現(xiàn)并不能滿足這種需求

  3. 需要首先創(chuàng)建IService類型的實(shí)例,然后才能使用UseCache和UseLogger等方法對(duì)其進(jìn)行設(shè)置,如果在實(shí)例的創(chuàng)建過(guò)程中存在對(duì)ICache或者ILogger的依賴的話(比如在構(gòu)造函數(shù)中希望能夠使用ILogger的實(shí)例寫(xiě)一些日志信息等),那么實(shí)現(xiàn)起來(lái)就會(huì)比較困難了

鑒于以上三點(diǎn)分析,當(dāng)需要在應(yīng)用程序或開(kāi)發(fā)框架中更為合理地引入Fluent Interface時(shí),上述簡(jiǎn)單的實(shí)現(xiàn)方式就無(wú)法滿足所有需求了。為此,我采用裝飾器模式,并結(jié)合C#的擴(kuò)展方法特性來(lái)實(shí)現(xiàn)Fluent Interface,這種方式不僅能夠解決上面的三種問(wèn)題,而且面向?qū)ο蟮脑O(shè)計(jì)會(huì)使Fluent Interface的擴(kuò)展變得更加簡(jiǎn)單。

使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)Fluent Interface

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

裝飾器模式的引入

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

1

2

3

4

public interface IConfigurator

{

    IUnityContainer Configure();

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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();

}

接下來(lái)就是針對(duì)不同的配置環(huán)節(jié)實(shí)現(xiàn)各自的配置器了。我們以緩存機(jī)制的配置為例,簡(jiǎn)要介紹一下“緩存配置器”的實(shí)現(xiàn)方式。

先定義一個(gè)名為ICacheConfigurator的接口,該接口實(shí)現(xiàn)了IConfigurator的接口,但它是一個(gè)空接口,并不包含任何屬性、事件或方法的接口定義。引入這個(gè)接口的目的就是要在接下來(lái)的擴(kuò)展方法定義中能夠?qū)崿F(xiàn)面向該接口的方法擴(kuò)展,于是上文中討論的第二個(gè)問(wèn)題就能引刃而解,這將在接下來(lái)的“擴(kuò)展方法的引入”部分進(jìn)行討論。事實(shí)上在很多成熟的應(yīng)用程序和框架中也有類似的設(shè)計(jì),比如將接口用作泛型約束類型等。因此,ICacheConfigurator的實(shí)現(xiàn)代碼非常簡(jiǎn)單:

1

2

3

public interface ICacheConfigurator : IConfigurator

{

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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方法中,首先調(diào)用配置上下文(也就是配置器本身所包含的上一層配置器實(shí)例)的Configure方法,同時(shí)獲得已配置的Unity IoC容器實(shí)例container,之后在container上繼續(xù)調(diào)用RegisterType方法,將給定的緩存機(jī)制實(shí)現(xiàn)類型注冊(cè)到container中,最后將container返回給調(diào)用者。

整個(gè)配置器部分的實(shí)現(xiàn),可以用下面的類圖進(jìn)行總結(jié):

在C#中如何使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)Fluent Interface

擴(kuò)展方法的引入

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

1

2

3

4

5

6

7

8

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的配置上采用基于字典的緩存機(jī)制,而WithConsoleLogger則表示在緩存配置的基礎(chǔ)上,還需要選用控制臺(tái)作為日志記錄機(jī)制。

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

接下來(lái)要做的,就是設(shè)計(jì)一個(gè)工廠類,使其能夠根據(jù)我們的配置信息創(chuàng)建一個(gè)新的IService實(shí)例。

工廠類的實(shí)現(xiàn)

工廠類的實(shí)現(xiàn)就非常簡(jiǎn)單了,同樣使用擴(kuò)展方法,對(duì)IConfigurator類型進(jìn)行擴(kuò)展,在獲得了Unity IoC容器的實(shí)例之后,只需要調(diào)用Resolve方法直接返回IService類型的實(shí)現(xiàn)類型就可以了。Resolve方法的使用,直接解決了上文中提到的第三個(gè)問(wèn)題。工廠類的代碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

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>();

    }

}

測(cè)試

創(chuàng)建一個(gè)測(cè)試項(xiàng)目以便對(duì)我們所做的工作進(jìn)行測(cè)試,比如下面的測(cè)試方法將會(huì)對(duì)IService的實(shí)現(xiàn)所采用的緩存機(jī)制類型和日志記錄機(jī)制類型進(jìn)行測(cè)試:

1

2

3

4

5

6

7

8

9

10

11

12

[TestMethod]

public void UseAppfabricCacheAndDatabaseLoggerTest()

{

    var service = ServiceFactory

        .ToConfig()

        .Service()

        .WithAppfabricCache()

        .WithDatabaseLogger()

        .Create();

    Assert.IsInstanceOfType(service.Cache, typeof(AppfabricCache));

    Assert.IsInstanceOfType(service.Logger, typeof(DatabaseLogger));

}

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

總結(jié)

通過(guò)對(duì)簡(jiǎn)單實(shí)現(xiàn)方式的討論,引出了可能存在的設(shè)計(jì)問(wèn)題,進(jìn)而選擇了一種更為合理的實(shí)現(xiàn)方式,即通過(guò)使用裝飾器模式和C#的擴(kuò)展方法特性來(lái)實(shí)現(xiàn)Fluent Interface。這種全新的實(shí)現(xiàn)方式不僅能夠解決所討論的設(shè)計(jì)問(wèn)題,而且這種面向?qū)ο蟮脑O(shè)計(jì)方式還為Fluent Interface的實(shí)現(xiàn)帶來(lái)了一定的可擴(kuò)展性。文章最后對(duì)這種實(shí)現(xiàn)方式進(jìn)行了簡(jiǎn)單測(cè)試,同時(shí)也展示了Fluent Interface在實(shí)際中的應(yīng)用。

上述內(nèi)容就是在C#中如何使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)Fluent Interface,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前名稱:在C#中如何使用裝飾器模式和擴(kuò)展方法實(shí)現(xiàn)FluentInterface
當(dāng)前網(wǎng)址:http://jinyejixie.com/article6/podsig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、關(guān)鍵詞優(yōu)化、網(wǎng)站內(nèi)鏈、企業(yè)建站、網(wǎng)站導(dǎo)航、標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

微信小程序開(kāi)發(fā)