Total Pageviews

Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

2015/09/03

De Morgan's laws (笛摩根定律) in programming

在命題邏輯和邏輯代數中,De Morgan’s Law [笛摩根定律 (或稱德摩根定理) ] 是關於命題邏輯規律的一對法則。奧古斯塔斯·德摩根首先發現了在命題邏輯中存在著下面這些關係:

  • 非(P 且 Q)=(非 P)或(非 Q)
  • 非(P 或 Q)=(非 P)且(非 Q)

如果忘記什麼是笛摩根定律,可以來複習一下高中數學,高一下數學2-1B觀念08集合的笛摩根定律

在撰寫程式中,一定會在商業邏輯中使用很多if-else的條件式,若善用De Morgan’s Law將其改寫,能將boolean 表示式以更清晰的方式表達

如以下兩個寫法,採用De Morgan’s Law以後,可讀性大幅提昇
1
2
3
4
5
6
7
 // 原本的寫法
 if (!(file_exists && !is_proteced)) {
 }

 // 改採De Morgan’s Law的寫法:把not分配到各項,再反轉and / or
 if (!file_exists || is_proteced) {
 }


Reference
[1] https://www.junyiacademy.org/math-grade-10/root/permutations-and-combinations_a/logic-set-and-arithmetic-theory/v/ymrcargLHN8
[2] https://zh.wikipedia.org/wiki/%E5%BE%B7%E6%91%A9%E6%A0%B9%E5%AE%9A%E5%BE%8B

2015/08/16

[Programming] 提高控制流程可讀性

最近閱讀了易讀程式之美一書,在提高控制流程可讀性此章節提到幾個重點

  1. 在使用if-else判斷式時,先處理肯定條件;先處理簡單的情況,必較能在畫面中同時呈現if與else區塊,在閱讀上很有幫助;先處理比較有趣或明顯的情況
  2. 縮短其他人理解程式所需的時間,比減少程式碼行數來得重要
  3. 只有在最簡單的情況下使用三元運算子(ternary operator),複雜的情況還是盡量使用if / else
  4. 避免使用do / while loop
  5. C++的創造者Bjarne Stroustrup說,在我的個人經驗裡,do / while 敘述經常是錯誤與誤解的來源,我寧願把條件式列在最前面,總而言之,我會盡量避免使用do / while 敘述
  6. 修改程式碼時要以全新的角度審視,退一步以整體的角度考慮程式碼
  7. 盡量消除loop中的巢狀結構,因為巢狀結構可讀性較差


Example1. 如果是很簡單的情況,用ternary operator是ok的
1
timeStr += (hour >= 12) ? "pm" : "am";

若是遇到較複雜的狀況,如下:
1
int result = exponent >= 0 ? mantissa * (1 << exponent) : mantissa * (1 << -exponent);

則建議將上述的表示方式改用if-else的方式表達
1
2
3
4
5
if (exponent >= 0) {
 result = mantissa * (1 << exponent);
} else {
 result = mantissa * (1 << -exponent);
}


Example2. 盡量避免用do-while

如原本為do-while的表示方式
1
2
3
4
5
int sum = 0;
int count = 1;
do {
    sum += count++;
} while (count <= 10);

除非有什麼特別原因,盡量改成while-loop
1
2
3
4
int sum = 0, count = 1;
while (count <= 10) {
 sum += count++;
}









2015/06/27

[Programming] 不被誤解的名稱

最近閱讀了易讀程式之美一書,在不被誤解的名稱此章節提到幾個重點

  1. 反覆思考名稱,自問別人會怎麼解釋這個名稱
  2. 對於邊界的極值而言,最清楚的方式是在名稱前面加上max_或min_
  3. 選擇boolean變數或回傳booleab值的函數名稱時,必須確保能清楚表示true / false的意義
  4. 一般來說,加上is / has / can / should 可以讓boolean value更加明確
  5. 避免在名稱中使用否定描述,如disableSSL,改用useSSL會比較容易閱讀且簡短
  6. 最好的名稱是最不容易被誤用的名稱,閱讀程式碼的人能夠清楚了解撰寫程式的人的意圖。不幸的是,許多英文單字在程式碼都會有兩種以上的解釋,如filter、length以及limit
  7. 在決定名稱前,要從反向思考,想像可能造成的誤解,最好的名稱能夠盡量避免誤會
  8. 對於定義數值的上下限,max與min是很有幫助的prefix;對於閉區間,first與last也是十分合適的prefix;對於半開放區間,begin與end是符合使用慣例的好選擇
  9. 注意使用者對特定單字的預期,如使用者會認為get與size是lightweight accessor


Example
1. 對於邊界的極值而言,最清楚的方式是在名稱前面加上max_或min_。以切字串為例,即長度第15個字以後以...表示,如"撰寫程式時,應該將讀者理解所需的時間降到最短"此段字串要回傳"撰寫程式時,應該將讀者理解所需..."

如在main method裡面分別呼叫clip與truncate兩個methods

1
2
3
 String str = "撰寫程式時,應該將讀者理解所需的時間降到最短";
 test.clip(str, 15);
 test.truncate(str, 15);

以下是clip method的第二個parameter的命名
1
2
3
 public String clip(String text, int length) {
  return text.substring(0, length).concat("...");
 }

以下是truncate method的命名,改成用maxLength,相較於前者,較為明確、清楚
1
2
3
4
 // 改成maxLength,會更加清楚明瞭
 public String truncate(String text, int maxLength) {
  return text.substring(0, maxLength).concat("...");
 }


2. 加上is / has / can / should 可以讓boolean value更加明確

1
2
3
4
5
 public Boolean authenticate(User user) {
  // do authentication, if failed
  Boolean checkPassword = Boolean.FALSE;
  return checkPassword;
 }

若將checkPassword改成如下,意義會更加直覺
1
2
3
4
5
 public Boolean authenticate(User user) {
  // do authentication, if failed
  Boolean userIsAuthnticated = Boolean.FALSE;
  return userIsAuthnticated;
 }




2015/06/21

[Programming] 富含資訊的名稱

最近閱讀了易讀程式之美一書,在富含資訊的名稱此章節提到幾個重點

  1. 程式碼應該易於理解
  2. 撰寫程式時,應該將讀者理解所需的時間降到最短
  3. 雖然減少程式碼數量是個很好的目標,但縮短理解時間更加重要
  4. 實務上,讓程式碼易於了解,往往也會產生良好的架構且易於測試
  5. 無論是重新命名變數、函數或類別,基本原則大同小異。名稱可視為簡短的註解,即使空間有限,透過選擇好的名稱就能包含許多資訊
  6. retval這類名稱不包含任何有意的資訊,只能代表return value,應該使用能夠說明便數值意義的名稱
  7. tmp這樣的名稱,只適用於變數生命週期很短,且作為暫存用途的變數
  8. 變數名稱不宜太長,名稱愈長就愈不容易記住,也會佔用更多畫面空間,甚至造成額外的自動換行


Example

1. 避免使用rtnVal,應給予有意義的名稱

以下是原本的寫法
1
2
3
4
5
Double rtnVal = 0d;
for (int i = 0; i < input; i++) {
 rtnVal += i * i;
}
return Math.sqrt(rtnVal);

以下是修改後的寫法,讓名稱具有意義
1
2
3
4
5
Double sumSquares = 0d;
for (int i = 0; i < input; i++) {
 sumSquares += i * i;
}
return Math.sqrt(sumSquares);

2. 避免使用tmp

以下是原本的寫法
1
2
3
4
5
public String demoForBadExample2(User user) {
 String tmp = user.getName();
 tmp += " " + user.getEmail();
 return tmp;
}

以下是修改後的寫法,userInfo比tmp傳達更多資訊
1
2
3
4
5
public String demoForGoodEaxmple2(User user) {
 String userInfo = user.getName();
 userInfo += " " + user.getEmail();
 return userInfo;
}

3. for-loop避免用 i, j, k 這樣的變數,尤其在nested loop你會改不清楚誰是誰

以下是原本的寫法
1
2
3
4
5
public void demoForBadExample3(List<User> users) {
 for (int i = 0; i < users.size(); i++) {
  System.out.println(users.get(i).toString());
 }
}


以下是修改後的寫法
1
2
3
4
5
public void demoForGoodExample3(List<User> users) {
 for (int user_i = 0; user_i < users.size(); user_i++) {
  System.out.println(users.get(user_i).toString());
 }
}



2015/06/18

JoSQL (SQL for Java Objects)

Requirement
If I have a Java collection as bellows:
1
2
3
Employee [empNo=1, name=Albert, gender=M, onBoardDate=Thu Feb 05 15:20:44 CST 2009]
Employee [empNo=2, name=Mandy, gender=null, onBoardDate=Fri Mar 05 15:20:44 CST 2010]
Employee [empNo=3, name=Verio, gender=M, onBoardDate=Thu Feb 05 15:20:44 CST 2009]

If I would like to utilize SQL statements to do search, which tool can help me?

Solution
JoSQL (SQL for Java Objects) provides the ability for a developer to apply a SQL statement to a collection of Java Objects. 
JoSQL provides the ability to search, order and group ANY Java objects and should be applied when you want to perform SQL-like queries on a collection of Java Objects.

Prerequisite
Configure the two jar files in your classpath:


Example
In this example, it will demonstrate:
  • find all employee objects
  • find all male employee objects
  • find employee objects which named "Mandy"
  • find all male employee object which onBoardDate between 2009/01/01 to 2009/12/31
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.josql.Query;
import org.josql.QueryExecutionException;
import org.josql.QueryParseException;
import org.josql.QueryResults;

import test.vo.Employee;

/**
 * The Class JoSqlTest.
 */
public class JoSqlTest {

    private List<Employee> employees = new ArrayList<Employee>();

    /**
     * The main method.
     * 
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {
        JoSqlTest test = new JoSqlTest();
        test.setupData();
        test.findAllEmployees();
        test.findAllMaleEmployees();
        test.findByEmployeeName("Mandy");
        test.findByGenderAndOnBoardDate("M", "2009/01/01", "2009/12/31");
    }

    /**
     * Select all employees.
     */
    public void findAllEmployees() {
        System.out.println("\n * find all employees");

        String sql = "select * from test.vo.Employee";

        // Create a new Query.
        Query query = new Query();
        try {
            // Parse the SQL you are going to use.
            query.parse(sql);

            // Execute the query.
            QueryResults qr = query.execute(employees);

            // Get the query results.
            List<Employee> result = qr.getResults();

            // Print search research
            for (Employee employee : result) {
                System.out.println(employee.toString());
            }

        } catch (QueryParseException e) {
            throw new RuntimeException(e);
        } catch (QueryExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Select all male employees.
     */
    public void findAllMaleEmployees() {
        System.out.println("\n * find all male employees");

        String sql = "select * from test.vo.Employee where gender = :gender";

        // Create a new Query.
        Query query = new Query();
        try {
            // Parse the SQL you are going to use.
            query.parse(sql);

            // set variable
            query.setVariable("gender", "M");

            // Execute the query.
            QueryResults queryResults = query.execute(employees);

            // Get the query results.
            List<Employee> result = queryResults.getResults();

            // Print search research
            for (Employee employee : result) {
                System.out.println(employee.toString());
            }

        } catch (QueryParseException e) {
            throw new RuntimeException(e);
        } catch (QueryExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Find by employee name.
     * 
     * @param name
     *            the name
     */
    public void findByEmployeeName(String name) {

        System.out.println("\n * find by employee name");

        String sql = "select * from test.vo.Employee where name = :name";

        // Create a new Query.
        Query query = new Query();
        try {
            // Parse the SQL you are going to use.
            query.parse(sql);

            // set variable
            query.setVariable("name", name);

            // Execute the query.
            QueryResults queryResults = query.execute(employees);

            // Get the query results.
            List<Employee> result = queryResults.getResults();

            // Print search research
            for (Employee employee : result) {
                System.out.println(employee.toString());
            }

        } catch (QueryParseException e) {
            throw new RuntimeException(e);
        } catch (QueryExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Find by gender and on board date.
     * 
     * @param gender
     *            the gender
     * @param fromDate
     *            the from date
     * @param toDate
     *            the to date
     */
    public void findByGenderAndOnBoardDate(String gender, String fromDate, String toDate) {
        System.out.println("\n * find By Gender And OnBoardDate");

        String sql = "select * from test.vo.Employee where gender = :gender "
                + " and onBoardDate between :fromDate and :toDate ";
        // Create a new Query.
        Query query = new Query();
        try {
            // Parse the SQL you are going to use.
            query.parse(sql);

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
            // set variable
            query.setVariable("gender", gender);
            query.setVariable("fromDate", dateFormat.parse(fromDate));
            query.setVariable("toDate", dateFormat.parse(toDate));

            // Execute the query.
            QueryResults queryResults = query.execute(employees);

            // Get the query results.
            List<Employee> result = queryResults.getResults();

            // Print search research
            for (Employee employee : result) {
                System.out.println(employee.toString());
            }

        } catch (QueryParseException e) {
            throw new RuntimeException(e);
        } catch (QueryExecutionException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Setup data.
     */
    public void setupData() {
        System.out.println("\n * setup data");

        Employee albert = new Employee();
        albert.setEmpNo(1L);
        albert.setName("Albert");
        albert.setGender("M");
        Calendar cal1 = Calendar.getInstance();
        cal1.set(Calendar.YEAR, 2009);
        cal1.set(Calendar.MONTH, 1);
        cal1.set(Calendar.DATE, 5);
        albert.setOnBoardDate(cal1.getTime());

        Employee mandy = new Employee();
        mandy.setEmpNo(2L);
        mandy.setName("Mandy");
        Calendar cal2 = Calendar.getInstance();
        cal2.set(Calendar.YEAR, 2010);
        cal2.set(Calendar.MONTH, 2);
        cal2.set(Calendar.DATE, 5);
        mandy.setOnBoardDate(cal2.getTime());

        Employee verio = new Employee();
        verio.setEmpNo(3L);
        verio.setName("Verio");
        verio.setGender("M");
        Calendar cal3 = Calendar.getInstance();
        cal3.set(Calendar.YEAR, 2009);
        cal3.set(Calendar.MONTH, 8);
        cal3.set(Calendar.DATE, 10);
        verio.setOnBoardDate(cal1.getTime());

        employees.add(albert);
        employees.add(mandy);
        employees.add(verio);
    }

}


Console:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 * setup data

 * find all employees
Employee [empNo=1, name=Albert, gender=M, onBoardDate=Thu Feb 05 16:00:48 CST 2009]
Employee [empNo=2, name=Mandy, gender=null, onBoardDate=Fri Mar 05 16:00:48 CST 2010]
Employee [empNo=3, name=Verio, gender=M, onBoardDate=Thu Feb 05 16:00:48 CST 2009]

 * find all male employees
Employee [empNo=1, name=Albert, gender=M, onBoardDate=Thu Feb 05 16:00:48 CST 2009]
Employee [empNo=3, name=Verio, gender=M, onBoardDate=Thu Feb 05 16:00:48 CST 2009]

 * find by employee name
Employee [empNo=2, name=Mandy, gender=null, onBoardDate=Fri Mar 05 16:00:48 CST 2010]

 * find By Gender And OnBoardDate
Employee [empNo=1, name=Albert, gender=M, onBoardDate=Thu Feb 05 16:00:48 CST 2009]
Employee [empNo=3, name=Verio, gender=M, onBoardDate=Thu Feb 05 16:00:48 CST 2009]

Reference
[1] http://josql.sourceforge.net/

2015/04/29

How to sort list of Java object with multiple fields

Problem
Assume I have a Java bean which named Fms432fa
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Data
public class Fms432fa implements Serializable {
    // default serial version id, required for serializable classes.
    private static final long serialVersionUID = 1L;

    private BigDecimal befalc1;
    private String accyr;
    private String dataType;
    private String rptNo;
    private BigDecimal alc1;
    private BigDecimal alc2;
    private BigDecimal alc3;
    private BigDecimal alc4;
    private BigDecimal alc5;
    private BigDecimal alc6;
    private BigDecimal alc7;
    private BigDecimal alc8;
    private BigDecimal alc9;
    private BigDecimal alc10;
    private BigDecimal alc11;
    private BigDecimal alc12;
    private String userid;
    private String updDateTime;

    /** {@inheritDoc} */
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

}

If I have a List of Fms432fa, and I would like to sort it by accyr, dataType and rptNo. How to do it?

Solution
Utilize Google Guava to fulfill this sorting requirement
1
2
3
4
5
6
7
8
        Collections.sort(fms432fas, new Comparator<Fms432fa>() {
            @Override
            public int compare(Fms432fa o1, Fms432fa o2) {
                return ComparisonChain.start().compare(o1.getAccyr(), o2.getAccyr())
                        .compare(o1.getDataType(), o2.getDataType())
                        .compare(o1.getRptNo(), o2.getRptNo()).result();
            }
        });


Reference
[1] http://blog.projectnibble.org/2013/06/21/ways-to-sort-lists-of-objects-in-java-based-on-multiple-fields/