2019年8月13日 星期二

Take advantage of variables

I think those two concepts (maybe call them philosophy) are the most useful techniques in Object-Oriented Design.

  • DRY: Extract repeated or similar functions and reuse them.
  • Single Responsibility: Keep your small chunk of code simple.

I would like to add one more thing that I usually call three of them "Good Code Smell"

  • Take advantage of variables.

We normally write code directly like this:

function checkScore {
  if (score > 50) {
    // do something
  }
}

When the condition becomes complicated:

function checkScore {
  if (score > 50 && score <= 100) {
    // do something
  }
}

More complicated:

function checkScore {
  if (score > 50 && score <= 100 && seconds > 60 && seconds <= 120) {
    // do something
  }
}

Well, all of them look ok, but how about this one:

function checkTenPercentOff() {
  if (
    // check qualified user
    user.title == 'Gold Member' && user.points > 1000 &&
    // check cart is not empty
    cartItems != null && cartItems != undefined && cartItems.length > 0 &&
    // check each item's quantity
    checkQuantity(cartItems) &&
    // check special sales period
    now > new Date('2019-09-01T00:00:00') && now <= new Date('2019-12-31T00:00:00')
  ) {
    // do 10% off for the user
  }
}

This is a monster if statement although it may still simple compared with real requirements from an SRS.

Now, we can take advantage of variable names.

function checkTenPercentOff() {
  // Look, we even don't need comments if we have good variable names
  var isQualifiedUser = user.title == 'Gold Member' && user.points > 1000;
  var isNotEmptyCart = cartItems != null && cartItems != undefined && cartItems.length > 0;
  var allItemsHaveQuantity = checkQuantity(cartItems);
  var isSpecialSalesPeriod = now > Date('2019-09-01T00:00:00') && now <= new Date('2019-12-31T00:00:00');

  var isQualifiedForTenPercentOff =
    isQualifiedUser &&
    isNotEmptyCart &&
    allItemsHaveQuantity &&
    isSpecialSalesPeriod;

  if (isQualifiedForTenPercentOff) {
    // do 10% off for the user
  }
}

It looks much clear and easy to read now.

Remember the DRY and Single Responsibility? How about this:

// extract variables as parameters, then you don't hard-code a function.

function checkQualifiedUser(title, points) {
  var isQualifiedUser = user.title == title && user.points > points;
  return isQualifiedUser;
}

function checkNotEmptyCart(cartItems) {
  var isNotEmptyCart = cartItems != null && cartItems != undefined && cartItems.length > 0;
  return isNotEmptyCart;
}

function checkAllItemsHaveQuantity(cartItems) {
  var allItemsHaveQuantity = checkQuantity(cartItems);
  return allItemsHaveQuantity;
}

function checkSpecialSalesPeriod(startDate, endDate) {
  var isSpecialSalesPeriod = now > startDate && now <= endDate;
  return isSpecialSalesPeriod;
}

function checkTenPercentOff() {
  var isQualifiedForTenPercentOff =
    checkQualifiedUser('Gold Member', 1000) &&
    checkNotEmptyCart(cartItems) &&
    checkAllItemsHaveQuantity(cartItems) &&
    checkSpecialSalesPeriod(new Date('2019-09-01T00:00:00'), new Date('2019-12-31T00:00:00'));

  if (isQualifiedForTenPercentOff) {
    // do 10% off for the user
  }
}

function checkFifteenPercentOff() {
  var isQualifiedForFifteenPercentOff =
    checkQualifiedUser('Diamond Member', 2000) &&
    checkNotEmptyCart(cartItems) &&
    checkAllItemsHaveQuantity(cartItems) &&
    checkSpecialSalesPeriod(new Date('2020-01-01T00:00:00'), new Date('2020-06-31T00:00:00'));

  if (isQualifiedForFifteenPercentOff) {
    // do 15% off for the user
  }
}

Now, we have reusable(DRY) and short(Single Responsibility) functions and clear variable names(Take advantage of variables)

2019年7月23日 星期二

Reuse, but don't make a huge function

  • The concept of DRY is super simple - do not duplicate codes if there are already in the program. The basic practice is using a function to do a particular action and let other places call the function if they need.
      // bad
      const name1 = 'Bob'
      const msg1 = 'Hi'
      console.log(`${name1} says: ${msg1}`)
    
      const name2 = 'Tom'
      const msg2 = `How's it going?`
      console.log(`${name2} says: ${msg2}`)
    
      // good
      function say(name, message) {
        console.log(`${name} says: ${message}`)
      }
    
      say('Bob', 'Hi')
      say('Tom', `How's it going?`)
    
  • However, once the requirement changed and we modify the function, of course, all function calls will be affected. That's good we save time.
      function say(from, to, message) {
        console.log(`${from} says ${to}: ${message}`)
      }
    
      say('Bob', 'John', 'Hi')
      say('Tom', 'Peter', `How's it going?`)
    
  • It is very common that we use parameters as inputs to let a function deal with for different situations.
      function say(from, to, message) {
        if (to !== null && to !== undefined) {
          console.log(`${from} says ${to}: ${message}`)
        } else {
          console.log(`${from} says: ${message}`)
        }
      }
    
      say('Bob', 'John', 'Hi')
      say('Tom', null, `How's it going?`)
    
  • You know, the requirement might change many times because customers are indecisive. Even if they don't, we still have to do refactoring in order to have better code quality. That means we might change codes.
      function say(from, to, message, isQuestion) {
        const ending = isQuestion ? '?' : '.'
        if (to !== null && to !== undefined) {
          console.log(`${from} says ${to}: ${message}${ending}`)
        } else {
          console.log(`${from} says: ${message}${ending}`)
        }
      }
    
      say('Bob', null, 'Hi', false)
      say('Tom', 'Peter', `How's it going`, true)
    
  • You will realize that functions have more complex parameters and conditions are very hard to modify. It calls "high coupling" if there are too many places rely on a heavy code.
      // the combinations you need to test in order to make sure your function works:
      say('name', null, 'message', false)
      say('name', 'name', 'message', false)
      say('name', null, 'message', true)
      say('name', 'name', 'message', true)
    
      // the combinations you may not expect that will ruin your function:
      say(null, 'name', 'message', false)
      say('name', 'name', null, true)
    
  • Therefore, when you are trying to DRY your code, be careful that don't put too many codes into one function. The function will be super complex and hard to maintain. It may be a good idea to have small functions instead.
      // there are actually 4 scenarios, so it may be a good idea to have 4 functions
      // new function names are more semantic
      // there are no magical boolean flag and optional parameters (reduce parameter mistakes)
      // we get rid of if conditions (reduce the complexity)
      // once one of the functions changed, we won't affect other functions
    
      function talk(name, message) {
        console.log(`${name} says: ${message}.`)
      }
    
      function askQuestion(name, message) {
        console.log(`${name} says: ${message}?`)
      }
    
      function talkToSomeone(name, someone, message) {
        console.log(`${name} says to ${someone}: ${message}.`)
      }
    
      function askQuestionToSomeone(name, someone, message) {
        console.log(`${name} says to ${someone}: ${message}?`)
      }
    
      talk('Bob', 'Hi')
      askQuestion('Tom', `How's it going`)
      talkToSomeone('Bob', 'John', 'Hi')
      askQuestionToSomeone('Tom', 'Peter', `How's it going`)
    
  • This example is overly simple, and the naming actually sucks, but I hope I give you a general idea of how to organize your functions.

2017年7月1日 星期六

一為全 全為一

資料庫如果面臨 資料具有狀態及歷程特性 且 需要因特定狀況下取消/復原狀態 或 差異比對

每次狀態變更 都新增一筆全新的紀錄 或許 比重複修改狀態欄位 來得簡單

這其實就是 前端 單向資料流 的概念嘛

2017年6月13日 星期二

Javascript 用雙波浪號 ~~ 來 屌炸天 轉型整數前請三思

Javascript 用雙波浪號 ~~ 來 屌炸天 轉型整數前請三思

tl;dr

曾幾何時看到別人 JS 寫雙波浪號 來把 string 轉型成 number 而且是整數

http://qa.helplib.com/84467

例如 ~~'123.456' = 123

其實效果跟 Math.floor('123.456') 一樣

某些瀏覽器可能用雙波浪寫法會快上一些

https://jsperf.com/tilde-vs-floor

然而 Number.MAX_SAFE_INTEGER 是 9007199254740991

照理來說 只要不超過這個數字 應該就沒事吧

事實上 只有 -2147483648 ~ 2147483648 範圍的值有作用

Bitwise operators treat their operands as a sequence of 32 bits (zeroes and ones), rather than as decimal, hexadecimal, or octal numbers. For example, the decimal number nine has a binary representation of 1001. Bitwise operators perform their operations on such binary representations, but they return standard JavaScript numerical values.
The numbers -2147483648 and 2147483647 are the minimum and the maximum integers representable through a 32bit signed number.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

簡單來說只要值超過 2147483648 就 GG 啦

我已經不知多少專案用了這個寫法 祈禱他們都只會小於 2147483648

如果有不幸 我相信接手的人都足夠機智 可以突破盲腸

2015年2月25日 星期三

關於 Linq To Xml 抓不到 XElement 的問題

Linq To Xml 很強大 用來處理 XML 十分方便

但是下面這個 RSS feed 卻無法順利抓到 XElement

http://www.google.com.tw/alerts/feeds/09781140062556884779/15827306603345874523





說穿了沒什麼 就是它的 XName 實際上叫 "{http://www.w3.org/2005/Atom}entry" 而非 瀏覽器上看到的 "entry"

但是第一次遇到的當下肯定覺得

X! 之前這樣寫明明就可以抓到 怎麼現在不行

著實惱人

stackoverflow





參考寫法: