乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      API設(shè)計的一些心得總結(jié)

       quasiceo 2013-02-20

      API設(shè)計的一些心得總結(jié)

      2009-09-28 09:55 來源:天極網(wǎng) 作者:老趙 博客園 責(zé)任編輯:鄭重·yesky 評論(0)

        你做過API設(shè)計么?不管你是否做過API設(shè)計,都不妨看看老趙的這篇博文。在這篇文章中,老趙總結(jié)了自己進(jìn)行API設(shè)計的一些心得。

        我平時的主要工作之一,便是編寫一些基礎(chǔ)及通用的類庫,能夠在項目中大量復(fù)用。換句話說,我的工作目的,是讓其他開發(fā)人員可以更好地完成工作。 因此,如何設(shè)計更容易使用的API是我經(jīng)常要考慮的東西,偶爾也會有一些體會。而現(xiàn)在這些內(nèi)容,是我在為Functional Reactive Programing寫“參考答案”的時候忽然“總結(jié)”出來的想法??赡鼙容^簡單,但我想也是設(shè)計API是需要考慮的一些內(nèi)容。

        在那篇文章里,我們是在為IEvent< T>對象提供一些封裝,其中會有MapEvent和FilterEvent等類型,為了方便調(diào)用,我們還定義了對應(yīng)的擴展方法:

      1. public class MapEvent< TIn, TOut> : InOutEventBase< TIn, TOut>  
      2. {  
      3.     public MapEvent(Func< TIn, TOut> mapper, IEvent< TIn> inEvent)  
      4.         : base(inEvent)  
      5.     {  
      6.         ...  
      7.     }  
      8. }  
      9.  
      10. public class FilterEvent< TEventArgs> : InOutEventBase< TEventArgs, TEventArgs>  
      11. {  
      12.     public FilterEvent(Func< TEventArgs, bool> predicate, IEvent< TEventArgs> inEvent)  
      13.         : base(inEvent)  
      14.     {  
      15.         ...  
      16.     }  
      17. }  
      18.  
      19. public static class EventExtensions  
      20. {  
      21.     public static MapEvent< TIn, TOut> Merge< TIn, TOut>(  
      22.         this IEvent< TIn, TOut> ev, Func< TIn, TOut> mapper)  
      23.     {  
      24.         ...  
      25.     }  
      26.  
      27.     public static FilterEvent< TEventArgs> Filter< TEventArgs>(  
      28.         this IEvent< TEventArgs> ev, Func< TEventArgs, bool> predicate)  
      29.     {  
      30.         ...  
      31.     }  
      32. }  
      33.  

        MergeEvent和FilterEvent都是對另一個Event對象的封裝,您可以當(dāng)作一種裝飾器模式來考慮。不知您觀察到?jīng)]有,這個 “待封裝”的Event對象在不同的地方(構(gòu)造函數(shù)或擴展方法),出現(xiàn)的位置是不同的。在擴展方法中,它是作為第一個參數(shù)出現(xiàn)在參數(shù)列表中,而在構(gòu)造函數(shù) 中它則是第二個參數(shù)。對于擴展方法來說,它是由語言規(guī)范強制得出的。但是在構(gòu)造函數(shù)中,這出現(xiàn)的順序完全可有由我們“自由”確定。那么,我們能否將待封裝 的Event對象作為構(gòu)造函數(shù)的第一個參數(shù)呢?

        自然是可以的,只是我在這里傾向于放在最后。原因在于這有利于API使用時的清晰。

        假如我們沒有擴展方法,也就是說只能使用構(gòu)造函數(shù)進(jìn)行“裝飾”,那么使用現(xiàn)在則是:

      1. var ev =  
      2.     new MapEvent< intstring>(  
      3.         i => i.ToString(),  
      4.         new FilterEvent< int>(  
      5.             i => i <  10,  
      6.             new MapEvent< DateTime, int>(  
      7.                 d => d.Millisecond,  
      8.                 ...)));  
      9.  

        有的時候,我會將Lambda表達(dá)式寫在上一行,這樣可以讓代碼更為緊湊。那么如果MapEvent和FilterEvent都把待封裝的Event對象作為構(gòu)造和函數(shù)的第一個參數(shù),又會怎么樣呢?

      1. var ev =  
      2.     new MapEvent< intstring>(  
      3.         new FilterEvent< int>(  
      4.             new MapEvent< DateTime, int>(  
      5.                 ...,  
      6.                 d => d.Millisecond),  
      7.             i => i <  10),  
      8.         i => i.ToString());  
      9.  

        對比這兩者,在我看來它們的信息“呈現(xiàn)方式”是有顯著差距的。對于第一種情況(Event作為構(gòu)造函數(shù)最后一個參數(shù)),用戶看到這個定義時,從上到下的閱讀順序是:

      1. 構(gòu)造一個MapEvent對象,映射方式是XXX
      2. 包含一個FilterEvent對象,過濾條件是YYY
      3. 包含一個MapEvent對象,映射方式是ZZZ

        而對于第二種情況(Event作為構(gòu)造函數(shù)的第一個參數(shù)):

      1. 構(gòu)造一個MapEvent對象
      2. 包含一個FilterEvent對象
      3. 構(gòu)造一個MapEvent對象
      4. 最內(nèi)層MapEvent的映射方式為ZZZ
      5. 上一層FiterEvent……
      6. ……

        第一種情況,API體現(xiàn)出的信息是流暢的,而第二種情況信息的體現(xiàn)是回溯的。第一種信息如“隊列”,而第二種如“棧”。第一種API閱讀起來用 戶視線是單向的,而第二種API用戶可能會去努力尋找某個Lambda表達(dá)式到底對應(yīng)著哪個對象——就像我們?yōu)槭裁刺岢玦f/for不應(yīng)該嵌套太深,因為 找匹配的大括號的確是件比較麻煩的事情。我想,應(yīng)該沒有會選擇把Event對象放在構(gòu)造函數(shù)參數(shù)列表的中間吧(如果有3個及參數(shù)),因為這會讓API調(diào)用 看起來成“鋸齒狀”,實在不利于閱讀。

        因此,在各種需要“裝飾”的場合,我往往都把“被裝飾者”作為構(gòu)造函數(shù)的最后一個參數(shù)。例如我在構(gòu)造DomainRoute的時候,便也是把 innerRoute作為構(gòu)造函數(shù)的最后一個參數(shù),由于DouteRoute所需要的參數(shù)較多,因此如果把innerRoute作為第一個參數(shù),看起來會 更加不便一些。同樣的,在之前設(shè)法“拯救C# 2.0”的時候也使用了這個做法。

        當(dāng)然,這些是我個人的看法,并非所有人都是這樣做的。例如在.NET Framework中負(fù)責(zé)GZip壓縮的GZipStream對象,它的構(gòu)造函數(shù)便是將innerStream作為第一個參數(shù)出現(xiàn)。幸好,C# 3.0中已經(jīng)有了擴展方法,如果使用構(gòu)造函數(shù)的話,即使信息再流暢,我想也不如擴展方法來的直觀。因此,我一般都會利用擴展方法,讓開發(fā)人員可以編寫這樣 的API:

      1. dateEvent.Map(d => d.Millisecond).Filter(i => i <  10).Map(i => i.ToString())  
      2. route.WithDomain("http://www.{*domain}/blogs"new { ... });  
      3. stream.GZip(CompressionMode.Compress).Encrypt(...);  
      4.  

        其實許多高級語言都會為了讓代碼寫的更易懂更清晰,因而提供一些看似“語法糖”的東西。例如F#中的|>操作符:

      1. let form = new Form(Visible = true, TopMost = true, Text = "Event Sample")  
      2. form.MouseDown  
      3.     |> Event.merge form.MouseMove  
      4.     |> Event.filter (fun args -> args.Button = MouseButtons.Left)  
      5.     |> Event.map (fun args -> (args.X, args.Y))  
      6.     |> Event.listen (fun (x, y) -> printfn "(%d, %d)" x y)  
      7.  

        其實|>操作符的目的只是把函數(shù)的最后一個參數(shù)調(diào)到之前來,但它能讓我們寫出“易讀”的代碼。例如FsTest類庫允許我們這樣寫:

      1. "foo" |> should equal "foo" 
      2.  

        但其實,從理論上說,這種寫法完全等價于:

      1. should equal "foo" "foo" 
      2.  

        正是因為有了|>操作符,F(xiàn)#在這種情況下會將待封裝的Event對象作為函數(shù)的最后一個參數(shù)。這便是語言特性對API設(shè)計的影響。此 外,F(xiàn)#中的“>>”以及Haskell的“.”可用“`”把一個函數(shù)作為中綴操作符來使用。但如果是Java這樣的語言,由于缺乏一些靈活 的語法特性,開發(fā)人員就只能靠框架和類庫來構(gòu)建“Fluent Interface”來度過難關(guān)了(如Google Collections)?!蹲坑谐尚У某绦騿T》一書中舉了這么一個例子,它們?yōu)橐粋€Car對象的構(gòu)造編寫了流暢接口:

      1. Car car = Car.describedAs().  
      2.              .box()  
      3.              .length(50.5)  
      4.              .type(Type.INSULATED)  
      5.              .includes(Equipment.LADDER)  
      6.              .lining(Lining.CORK);  
      7.  

        以代替呆板的Java語法:

      1. Car car = new CarImpl();  
      2. MarketingDescription desc = newMarketingDescriptionImpl();  
      3. desc.setType("Box");  
      4. desc.setSubType("Insulated");  
      5. desc.setAttribute("length""50.5");  
      6. desc.setAttribute("ladder""yes");  
      7. desc.setAttribute("lining type""cork");  
      8. car.setDescription(desc)  
      9.  

        似乎程序員永遠(yuǎn)不會放棄這方面追求:編寫更清晰,更易懂的代碼。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多