2017年12月11日 星期一

如何不寫註解也看得懂程式

當一個人再開發程式的時候,寫的程式碼自己看得懂就好
但多人開發的時候,你的程式碼不只要給自己看,別人也要看
所以有些公司比較要求會有Code Review,來確保持程式碼有一定的水準

怎樣的程式不好閱讀?我覺得是以下兩個最常發生

  • 亂命名
  • 方法超過50行

其實我覺得當一個方法超過50行,裡面又包含上百行if else
這種程式碼看起來就相當累,也是在浪費生命
就如以下範例假設有一個列表的頁面需要輸入很多關鍵字來進行搜尋
所以搜尋頁面會有以下的程式

namespace Example.Controllers
{
    public class OrderController : Controller
    {
        // GET: Order
        public ActionResult Index(OrderArgs args)
        {
            using(var db = new NORTHWNDEntities())
            {
                var query = db.Orders.AsQueryable();

                //搜尋CustomerID
                if (string.IsNullOrEmpty(args.CustomerID))
                    query = query.Where(x => x.CustomerID.Contains(args.CustomerID));

                //搜尋OrderDate
                if (args.OrderStartDate != null && args.OrderEndDate != null)
                    query = query.Where(x => x.OrderDate >= args.OrderStartDate && x.OrderDate <= args.OrderEndDate);

                //搜尋ShipName
                if (string.IsNullOrEmpty(args.ShipName))
                    query = query.Where(x => x.ShipName.Contains(args.ShipName));

                //搜尋ShipAddress
                if (string.IsNullOrEmpty(args.ShipAddress))
                    query = query.Where(x => x.ShipAddress.Contains(args.ShipAddress));

                //搜尋ShipCity
                if (string.IsNullOrEmpty(args.ShipCity))
                    query = query.Where(x => x.ShipCity.Contains(args.ShipCity));

                return View(query.ToList());
            }            
        }
    }
}

這段Code在寫若有關鍵字傳進來,就對這個關鍵字做搜尋
其實這樣有註解、沒有複雜的邏輯也算還滿容易看得懂
那如果每個關鍵字裡面又有很多邏輯要判斷就會變成以下程式碼

namespace Example.Controllers
{
    public class OrderController : Controller
    {
        // GET: Order
        public ActionResult Index(OrderArgs args)
        {
            using(var db = new NORTHWNDEntities())
            {
                var query = db.Orders.AsQueryable();

                //搜尋CustomerID
                if (string.IsNullOrEmpty(args.CustomerID))
                {
                    /*
                    超多邏輯判斷
                    ............
                    ............
                    */
                    query = query.Where(x => x.CustomerID.Contains(args.CustomerID));
                }
                    

                //搜尋OrderDate
                if (args.OrderStartDate != null && args.OrderEndDate != null)
                {
                    /*
                    超多邏輯判斷
                    ............
                    ............
                    */
                    query = query.Where(x => x.OrderDate >= args.OrderStartDate && x.OrderDate <= args.OrderEndDate);
                }
                

                //搜尋ShipName
                if (string.IsNullOrEmpty(args.ShipName))
                {
                    /*
                    超多邏輯判斷
                    ............
                    ............
                    */
                    query = query.Where(x => x.ShipName.Contains(args.ShipName));
                }
                

                //搜尋ShipAddress
                if (string.IsNullOrEmpty(args.ShipAddress))
                {
                    /*
                    超多邏輯判斷
                    ............
                    ............
                    */
                    query = query.Where(x => x.ShipAddress.Contains(args.ShipAddress));
                }
                    

                //搜尋ShipCity
                if (string.IsNullOrEmpty(args.ShipCity))
                {
                    /*
                    超多邏輯判斷
                    ............
                    ............
                    */
                    query = query.Where(x => x.ShipCity.Contains(args.ShipCity));
                }                    

                return View(query.ToList());
            }            
        }
    }
}

是不是開始感覺有點雜亂了,當你這樣寫出來後面要修改你程式的人
又在加一些邏輯上去,一個方法破百行的成就就解開了
解開之後當然要繼續解破千行的成就= =,要如何讓他好閱讀呢
首先我把各種搜尋關鍵字寫成擴展,讓各種關鍵字複雜的邏輯切開,關注點分離
將每個複雜的搜尋都放在各別的擴展方法裡面

namespace Example.Models
{
    public static class OrderExtensions
    {
        public static IQueryable<Order> WhereByCustomerID(this IQueryable<Order> order, string customerID)
        {

            if (string.IsNullOrEmpty(customerID))
                return order;
            /*
            超多邏輯判斷
            ............
            ............
            */
            return order.Where(x => x.CustomerID.Contains(customerID));
        }

        public static IQueryable<Order> WhereByOrderDate(this IQueryable<Order> order, DateTime? StartDate, DateTime? EndDate)
        {

            if (StartDate == null || EndDate == null)
                return order;
            /*
            超多邏輯判斷
            ............
            ............
            */
            return order.Where(x => x.OrderDate >= StartDate && x.OrderDate <= EndDate);
        }

        public static IQueryable<Order> WhereByShipName(this IQueryable<Order> order, string ShipName)
        {

            if (string.IsNullOrEmpty(ShipName))
                return order;
            /*
            超多邏輯判斷
            ............
            ............
            */
            return order.Where(x => x.ShipName.Contains(ShipName));
        }

        public static IQueryable<Order> WhereByShipAddress(this IQueryable<Order> order, string ShipAddress)
        {

            if (string.IsNullOrEmpty(ShipAddress))
                return order;
            /*
            超多邏輯判斷
            ............
            ............
            */
            return order.Where(x => x.ShipAddress.Contains(ShipAddress));
        }

        public static IQueryable<Order> WhereByShipCity(this IQueryable<Order> order, string ShipCity)
        {

            if (string.IsNullOrEmpty(ShipCity))
                return order;
            /*
            超多邏輯判斷
            ............
            ............
            */
            return order.Where(x => x.ShipCity.Contains(ShipCity));
        }
    }
}

所以Controller的程式碼變得很有感覺,就算不寫註解您們看得懂嗎?

namespace Example.Controllers
{
    public class OrderController : Controller
    {
        // GET: Order
        public ActionResult Index(OrderArgs args)
        {
            using(var db = new NORTHWNDEntities())
            {
                var query = db.Orders.AsQueryable()
                    .WhereByCustomerID(args.CustomerID)
                    .WhereByOrderDate(args.OrderStartDate, args.OrderEndDate)
                    .WhereByShipName(args.ShipName)
                    .WhereByShipAddress(args.ShipAddress)
                    .WhereByShipCity(args.ShipCity);
                return View(query.ToList());
            }            
        }
    }
}

參考網址:
http://www.cnblogs.com/leotsai/p/how-to-write-beautiful-query-code.html

沒有留言:

張貼留言