只有累積,沒有奇蹟

2019年9月23日 星期一

[CheatSheets] ASP.NET MVC 5 Pipeline

前言 
在 ASP.NET 開發時生命週期 Life cycle 一直是個很重要的議題,在過去自己為了加深這方面的印象分別針對 ASP.NET Application 寫了篇文章 : [.NET] ASP.NET Application 概述,以及古老時代在開發 WebForm 時寫的 : [.NET] ASP.NET 網頁生命週期 Page Life-Cycle,在撰寫文章的過程中也加深了自己對於 pipeline 以及 Page life cycle 的印象,最近一直在維護既有的專案,在開發專案過程中發現自己久沒碰開始對 ASP.NET MVC 有點生疏起來,找到篇講解 ASP.NET MVC Pipeline 很清楚的文章 : Detailed ASP.NET MVC Pipeline,看完覺得收穫良多特記錄在部落格中,做為日後小抄使用 :)

2019年9月22日 星期日

[JavaScript] JavaScript Console.Log 的兩三事

前言
身為一位走跳社會多年的後端打雜工程師,對於前端技能一直處於荒廢加半放棄的狀態,但在工作上常常會遇到與前端工程師搭配合作的情境,因此前端基本的技能還是多少會需要略懂略懂,在前端進行偵錯時自己都會使用瀏覽器所提供的 console.log 來記錄 debug 資訊,但在最近發現 console API 新增了許多方法,可以讓開發者在 debug 時更為方便,此文章就來分享閱讀完 console API from MDN doc 的心得,包含 console 基本應用、console groupconsole tableconsole time 內容包含上述 API 介紹範例若有問題或是錯誤的地方歡迎各方高手大大一起討論或給予指導

Console 基本應用
最基本的應用是使用 console.log 紀錄內容,使用該 API 後會將內容輸出到瀏覽器的控制台,首先一開始

console.log
在 switch case 看到在進行判斷時是使用 string 作為依據,但在可能會存在像是判斷大小寫、打錯字、去
最基本的應用是使用 console.log 紀錄內容,使用該 API 後會將內容輸出到瀏覽器的控制台,首先一開始先來最基本的 Hello world
console.log('Hello world') 
// output : Hello world
log 內容也支援物件輸出,舉例來說定義 book 物件以及其屬性 id 和 name 兩種
var book = { id:'1', name:'This is a book'}
console.log(book);
// output : 
// {id: "1", name: "This is a book"}
//     id: "1"
//     name: "This is a book"
如果有多個變數需要一起輸出,又不希望透過每一行來執行時,可以透過  { }  將輸出內容包含在裡面
var book = { id:'1', name:'This is a book'};
var book2 = {id:'2', name:'Not a pen'};
console.log({book, book2});
// output : 
// {id: "1", name: "This is a book"}
// {id: "1", name: "Not a pen"}
也可以使用 C# 類似 string.format 方式將輸出其變數值
var name = 'Marcus 學習筆記';
console.log('welcome %s', name);
// output : welcome Marcus 學習筆記

console.warn 和 console.error
有時候輸出的 log 可能依據不同的 log 內容來做分類及等級顯示,如果紀錄的 log 內容等級較高可以使用 warn 以及 error 等 API,以增加閱讀 log 時的可讀性,舉例來說輸入以下內容
console.warn('Hello warn');
console.error('Hello error');
在 Chrome 瀏覽器會輸出以下內容

Console Group
當 console 輸出內容變多時就會不容易閱讀,這時可以使用 console.Group API 將相關訊息做分類顯示,方便在閱讀時更容易,使用  console.group  做為區塊的開始,接著在用  console.groupCollapsed  做為區塊的結尾,舉例來說購物車的應用情境如下
console.group('Shopping Cart');
console.log('user: Marcus');
// item 1
console.group('Shopping item');
console.log('Name: .NET Core in actopn');
console.log('Author: Andrew Lock');
console.log('ISBN: 1617294616');
console.groupEnd();
// item 2
console.group('Shopping item');
console.log('Name: Something about ASP.NET Core');
console.log('Author: Marcus');
console.log('ISBN: 9527');
console.groupEnd();
以上代碼輸出在 chrome 顯示如下

Console Table
使用 console.group 可以大幅提升 log 的可讀性,增加可讀性的另一種方式是使用 console Table,使用方式也相當簡單,使用  console.table  加上變數即可快速使用,以一開始的例子變數 book、book2 做為範例將 console.log 改為 console.table 即可
var book = { id:'1', name:'This is a book'};
var book2 = {id:'2', name:'Not a pencil'};
console.table({book, book2});
以上代碼輸出在 chrome 輸出如下


Console.Time
在 Gecko 加入 console.Time 使用計時器功能,在同一頁面最多可以使用 10,000 個計時器,單位則是以毫秒為單位,使用  console.time()  方法可以啟用,停止則是  console.timeend  ,計時器範例如下
console.time("計時");
alert("準備開始 ! ");
console.timeLog("計時");
alert("時間分支");
console.timeEnd("計時");
畫面上會呈現準備開始按鈕,計時完畢後會在控制台區塊顯示花費時間
計時: 4174.994873046875ms
計時: 4851.634033203125ms

Other
在 console 中還有其他好用的功能如下,這裡就不在多加描述有興趣可以嘗試看看
  • console.clear : 清除控制台
  • console.trace : 將物件輸出至控制台
如果想了解更多細節,可以參考  developer.mozilla.org 中的 Console API 詳細介紹,希望透過以上的介紹可以讓各位對於 Console 有更多認識 (還是只有我不知道 XDD) ,謝謝

2019年9月18日 星期三

[C#] Refactor - 重構 Switch case 陳述句

前言
在開發程式時使用判斷式是很常見的事情,簡單的判斷式可以使用 if 達到目的,但遇到多個條件或是三個以上時就可以使用 switch case 來解決此問題,但隨著需求的新增團隊可能在 switch case 的每個判斷句又加了很多新代碼,(最近在維護公司的專案上很常見到感觸很深),舉例來說可能代碼會長得像下面這樣
這篇就針對此問題重構方式做簡單介紹,若有更好的方式歡迎隨時提出來一起討論。

重構
從上述代碼可以感受到會有程式碼的壞味道,舉例來說可能存在以下問題
  • 閱讀上會較難閱讀
  • 加 case 時候時需要在這段 code 繼續增加情境來滿足需求,EX : 新增黑豹,這段代碼又要多好幾行
  • 如果隱藏各自 case 所需要做的事情,代碼描述上會更為明確
  • 要針對 switch case 重構有很多種方法,像是 Strategy pattern、Visitor pattern,但自己遇到這問題時還是最推薦使用 Dictionary 方式來進行重構,語意較為清楚團隊在看代碼時也不會太難懂,簡單說明步驟如下

    定義 Enum
    在 switch case 看到在進行判斷時是使用 string 作為依據,但在可能會存在像是判斷大小寫、打錯字、去空白等一些小問題,因此自己在遇到此問題的第一步會先將 switch 需要判斷的項目抽成 Enum,這樣在後續會更方便使用,也不會有打錯字的問題發生
    public enum Hero
    {
        Thanos,
        CaptainAmerica,
        IronMan,
        Thor,
        DoctorStrange,
        Hulk,
        SpiderMan
    }

    定義 Lookup
    定義完 Enum 之後,接著我們可以透過  Dictionary  定義 switch case 中所要執行的動作,在 lookup 字典中 key 為剛剛新增的 Enum 類別,value 在範例中是透過 console 因此使用 action 委派,將這些要執行的動作逐一的加到自訂的 _heroHandleLookup 中
    private Dictionary<Hero, Action> _heroHandleLookup;
    private void InitialHandler()
    {
        _heroHandleLookup = new Dictionary<Hero, Action>()
        {
            {Hero.Thanos, ()=> Console.WriteLine("I am villainous") },
            {Hero.CaptainAmerica, ()=> Console.Write("Captain America")},
            {Hero.IronMan, ()=> Console.Write($"I am IronMan") },
            {Hero.Thor, ()=> Console.Write($"I am Thor") },
            {Hero.DoctorStrange, () => Console.Write("I am DoctorStrange")},
            {Hero.Hulk, () => Console.Write("我綠綠的") },
            {Hero.SpiderMan, () => Console.Write("我會不自覺劇透")}
        };
    }

    取代 switch case
    在回到原本的代碼,可以透過 dictionary.containsKey 先判斷要執行的 Enum 是否存在 lookup 中,如果有的話就使用定義好的 action 委派來執行,代碼調整如下
    static void Main(string[] args)
    {
        InitialHandler();
    
        var hero = Hero.IronMan;
    
        if (_heroHandleLookup.ContainsKey(hero))
        {
            _heroHandleLookup[hero].Invoke();
        };
    }
    輸出結果,宣告重構成功 ! 
    I am IronMan

    重構 - 使用 Func
    透過以上描述,相信對於透過 dictionary 來重構複雜的 switch case 有一定的了解,但現實生活中往往是殘酷的,Production 的代碼中不會只有 console.write 那麼的簡單,很常會遇到 switch case 有回傳值的狀況發生,如果熟悉 LINQ 的朋友可以了解在 LINQ 很多 API 設計上都有用到 Func 的特性來讓開發者在使用時更為彈性,Action 與 Func 的差異差在 Func 有回傳值 Action 沒有,因此如果當有回傳值的時候就可以從原本的 Action 改用 Func 委派來解決回傳值的問題,代碼調整如下
    private static Dictionary<Hero, Func<string,string>> _heroHandleLookup;
    private static void InitialHandler()
    {
        _heroHandleLookup = new Dictionary<Hero, Func<string,string>>()
        {
            {Hero.Thanos, s => $"I am {s}" },
            {Hero.CaptainAmerica, s=> "Captain America"},
            {Hero.IronMan, s => "I am IronMan" },
            {Hero.Thor, s => "I am Thor" },
            {Hero.DoctorStrange, s => "I am DoctorStrange"},
            {Hero.Hulk, s => $"{s} 綠綠的rrrrrrrrr" },
            {Hero.SpiderMan, s => "我會不自覺劇透"}
        };
    }
    
    static void Main(string[] args)
    {
        InitialHandler();
    
        var hero = Hero.Hulk;
        var result = string.Empty;
    
        if (_heroHandleLookup.ContainsKey(hero))
        {
            result = _heroHandleLookup[hero].Invoke(hero.ToString());
        };
    
        Console.WriteLine(result);
        Console.ReadKey();
    }
    代碼說明如下
  • lookup 的 Dictionary 的 value 由 action 改為 Func<string,string>
  • 以此情境來說 Func 第一個參數為要帶入的參數,第二個參數為回傳值
  • 透過 lambda 指定各自的回傳值,舉例來說 Iron 就回傳 I am IronMan,Hulk 回傳 {參數} 綠綠的rrrrrrrrr
  • 在使用端使用 result 來接回傳值,並統一透過 console 顯示回傳結果
  • 在重新執行一次結果並改用 hunk,輸出結果為 
    Hulk 綠綠的rrrrrrrrr

    感想
    會寫這一篇是因為最近大部分時間是在公司既有專案的維護,看到專案中有很多 code smell 程式碼的壞味道,像是 switch case 就是其中一大項目之一,雖然之前上課就學過使用此重構技巧,但對此特別有感而發希望將此過程記錄下來,希望這篇文章可以幫助到有興趣的朋友,如果有不清楚的地方歡迎一起討論,hope it helps !


    參考
    Saving a few lines of code. Part IV - Refactoring switch statements
    How to work with Action, Func, and Predicate delegates in C#

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com