iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 14
0
Software Development

如何讓程式碼告訴你他在做什麼?系列 第 15

Day-14 程式因為不想當邊緣人,所以.....

在語言的文法上,有所謂的介系詞

其目的在於讓前述的事物可以與其他詞句連貫,並表達其作用關係

所以我們在描述需求或是程式的時候

勢必也需要同樣的作法

通常這樣的文字並不適合放在前兩篇所描述的行為與事物

不然也不會特別叫介系詞了

例如像是我們前面所做的範例

Crawler 中有 HttpClient與 HtmlLoader

而其前後又還有 Action<HttpRequestMessage>IEnumerable<HtmlElement>

所以為了將這幾個內容依序連貫起來,所以用了Chain<>ChainAwaiter<>

而這次要來說的就是 Then<T> 這個東西

也就是說,每一個功能其實並不需要自己對外宣稱可以如何來串接

我們只需要用擴充方法來加以串接即可

而這件事情勢必會牽涉到串接進出口的模式

隨著模式的不同和串接的不同

所以我們先來看有哪些 Input 與 Output 模式

這裡我提幾個現在API上會比較常見的

  • Input

    • GetAsync(HttpRequestMessage request)

      • 這種屬於已非常明確地公開相依型別的參數

        • 優點:清楚明確,無須額外轉換可直接使用,且為常用型別
        • 缺點:強制使用者相依於此型別的模組
    • GetAsync(Action<HttpRequestMessage> config)

      • 範例所使用的方式,讓使用者提供設定的方法而不是建立好的物件

        • 優點:物件建立並非由使用者建立,且為常用型別,所以非公開建構子型別的參數帶入很適合
        • 缺點:同上
    • GetAsync(Action<HttpRequestBuilder> config)

      • 這作法類似於HtmlElement的概念,讓使用者把資訊建立在 Builder 上,這樣就可以很合理的用 Builder 來建構我們所需要用的物件,而使用者也無須關注是如何達成的

        • 優點:無上述兩種類型的相依性缺陷
        • 缺點:Builder如何使用,設計不良的情況下會無法有足夠好的成效
    • GetAsync(IConfigureOptions<HttpRequestMessage> config)

      • 這是.Net Core Options Model中的型別IConfigureOptions<>,其成員只有公開void Configure(TOption option),所以與前述第二和三個一樣,只是這次用了物件加以包裹

        • 優點:更適合OOP的概念,而且可以區分各種Action<>代表之意義
        • 同樣相依於第三方型別問題
          • 解決方案:同樣於上述的做法,只是這次改更換整體的型別名稱,ex:IHttpRqeuestConfigureOptions
    • GetAsync<TRequest>(TRequest request)/GetAsync<TRequest>(Action<TRequest> config)

      • 透過泛型來提供參數的一種方式,一般來說除非真的能夠泛用在各種型別上時可能會看到這種類型的Input

        • 優點:操作自由度偏高,而且無須額外包裹物件或委派
        • 缺點:操作者既然自由度高就表示程式得承擔相關判斷
          • 解決方案:視情況加個約束條件,或還是改用介面的做法會比較妥當
    • GetAsync()

      • 往往這個意思表示,所需要的設定或物件已經於物件建構時載入,你只需要Invoke

        • 優點:操作容易,簡單易懂,而且沒有多餘的贅述
        • 缺點:如何Get沒有放在此處,如果直接由使用者引用會不知道是在Get什麼東西 所以你還記得你設定了什麼嗎?
            - 解決方案:既然此處已與使用者無關,不如放在裡面讓外部的RunAsync之類的方法來呼叫吧

基本上上述的方式都各有優缺點,而且實際上的確也都可能看的到

下面舉兩個實際案例

Moq4

var lovable = Mock.Of<ILoveThisFramework>(l => l.DownloadExists("2.0.0.0") == true);

Mock.Get(lovable)
    .Verify(framework => framework.DownloadExists("2.0.0.0"));

Mock.Of 也是一種介系詞的模式,這個後面會提及

Mock.Get(lovable) 即為泛型的方式,而這裡只是因為編譯器的型別推斷(Type Inference),所以我們不用去詳述其型別

而其後續接Verify驗證並帶入如何驗證

所以這樣的套件作法再加上適合的開發方式,就可以很容易寫出簡單易懂的測試

chocolatey

var config = new ConfigureBuilder<ChocolateyConfiguration>()
    .Set(c => c.PackageNames = "docker")
    .Set(c => c.CommandName = "list")
    .Build();

chocolatey.Lets.GetChocolatey()
    .Set(config.Configure)
    .List<IResult>()
    .SelectMany(result => result.Messages,(result,msgs) => msgs.Message)
    .Aggregate(Console.WriteLine,(write,msg) => write(msg));

chocolatey 是windows版的apt-get,而理論上操作通常都是用PowerShell

但仍然可以在NuGet上找到 chocolatey.lib

所以,上面這code 是我參考 chocolatey.lib 來嘗試的程式

在這次的我的寫法中也仍然是屬於泛型的做法,只是泛型型別再class上

chocolatey 的Set 則是屬於第三種作法,自訂型別的Action<>

而他提供的是委派型別,並不表示我就得寫Lambda在裡面

我的ConfigureBuilder在Builder之後的物件,是IConfigureOptions<>

所以透過類似委派型別的方式來達到自然融入套件的效果也是Action<> 這種類型委派的優點

但後續的List<>就很苦手了,IResult我找了好一陣子,而且還不一定對 是的,這段code其實沒有反應

所以這也算是一種UX上的缺陷

但.....chocolatey 並沒有推廣他的 C#函式庫,所以某種層面上這樣的寫法可以團隊內部約定

所以就算對外人有UX問題,那等要對外的時候才算數吧

備註


  • 如您目前是團隊工作,則請遵守團隊所訂下的撰寫規範,此篇文章僅為參考建議使用
  • 選擇第三方套件建議檢查對方是否有測試,除了可以掛保證外,如果不知道要怎麼用也可以從測試得知,畢竟測試即需求
  • chocolatey個人大推,就連visual studio 這種大型軟體都可以自動安裝到好
  • SelectMany(選擇一目標IEnumerable,對前述的目標做Select作為Result)
  • Aggregate(起始與回傳物件, (前一次結果, 這次的物件) => 執行方法內容)
    • 而這裡沒寫到的是我有實作一個擴充方法來輔助這個擴充,就是讓執行方法結束後直接回傳前一次結果,所以才會與常見的做法不一樣,而從頭到尾都是Console.Write這個Action<string>在來回傳遞

上一篇
Day-13 命名也講三字經!?
下一篇
Day-15 多國語言!?
系列文
如何讓程式碼告訴你他在做什麼?31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言