分類
Android

android ndk 主要元件以及結構

android ndk 主要的元件

  • 交叉編譯器(cross compiler):ARM , x86 , MIPS
  • 構建系統
  • Java native interface .h 檔案
  • C 函式庫
  • Math 函式庫
  • POSIX 執行緒
  • 小型的 C++ 函式庫
  • ZLib函式庫
  • 動態連結函式庫
  • Android Log 函式庫
  • Android NDK native APIs
  • OpenGL ES 3D
  • OpenSL ES
  • OpenMAX AL

 

android ndk 結構中一些比較重要的目錄以及腳本

  • ndk-build:為 ndk 構件系統的起始點。
  • ndk-gdb:可以使用 GNU Debugger 測試原生元件。
  • ndk-stack:原生組件崩潰時的追蹤堆棧。
  • build 目錄:包含所有 ndk 的 module。
  • platforms 目錄:包含不同 android 版本的 .h 檔以及 library
  • samples 目錄:範例
  • sources 目錄:共享 modules
  • toolchains 目錄:交叉編譯器,目前支援 ARM , X86 , MIPS

 

android ndk 專案重要的目錄

  • jni 目錄:為NDK項目的目錄。包含原生組件的 source code 以及 android.mk 檔。
  • libs 目錄:在 android ndk 建立過程中產生,包含指定的平台的獨立子目錄 e.g. ARM的armeabi。本目錄會打包在APK中。
  • obj 目錄:包含編譯 source code 之後所產生的目標文件。
分類
eclipse plugins

使用 Command Line 執行 Eclipse Format

避免違反 code standard,在 git push 之前,我一定小心翼翼的檢查每個要 push 的 java code 是否有執行 format(為了去除討厭的空白)。
但 eclipse ide 沒辦法針對多數檔案一併執行 format 動作,但改成command line來執行 format 動作就沒這個問題。
 
幾個步驟:
1.準備 eclipse 可執行檔,這個可執行檔就是你每天在寫程式開的eclipse 執行檔。
2.準備 org.eclipse.jdt.core.prefs 檔案,這個檔案紀錄你設定的format格式,檔案位置在專案根目錄下的 .settings/ 。
3.使用以下指令啟動

/path/of/eclipse -nosplash -application org.eclipse.jdt.core.JavaCodeFormatter -verbose -config /path/of/org.eclipse.jdt.core.prefs /path/of/file/you/want/to/format

Note:
/path/of/eclipse :eclipse 可執行檔路徑
/path/of/org.eclipse.jdt.core.prefs:org.eclipse.jdt.core.prefs 檔案路徑
/path/of/file/you/want/to/format:想要format 的檔案路徑,你可以指定檔案所在資料夾,formatter會幫你一併format 所有的檔案。
 
執行輸出

Configuration Name: /path/of/org.eclipse.jdt.core.prefs
Starting format job ...
Formatting: /path/of/file/you/want/to/format
Done.

 
 
 
 

分類
git 使用紀錄

git 建立自定義指令(git customize command)

使用 git branch 指令建立 branch 有個小缺點,就是branch name 不可以摻雜空格。e.g.

git branch branchname can not contain space

就會出現以下提示

usage: git branch [options] [-r | -a] [--merged | --no-merged]
   or: git branch [options] [-l] [-f] <branchname> [<start-point>]
   or: git branch [options] [-r] (-d | -D) <branchname>...
   or: git branch [options] (-m | -M) [<oldbranch>] <newbranch>
Generic options
    -v, --verbose         show hash and subject, give twice for upstream branch
    -t, --track           set up tracking mode (see git-pull(1))
    --set-upstream        change upstream info
    --color[=<when>]      use colored output
    -r, --remotes         act on remote-tracking branches
    --contains <commit>   print only branches that contain the commit
    --abbrev[=<n>]        use <n> digits to display SHA-1s
Specific git-branch actions:
    -a, --all             list both remote-tracking and local branches
    -d, --delete          delete fully merged branch
    -D                    delete branch (even if not merged)
    -m, --move            move/rename a branch and its reflog
    -M                    move/rename a branch, even if target exists
    --list                list branch names
    -l, --create-reflog   create the branch's reflog
    --edit-description    edit the description for the branch
    -f, --force           force creation (when already exists)
    --no-merged <commit>  print only not merged branches
    --merged <commit>     print only merged branches

 
搜尋了一下似乎沒有很直觀的解決方式。決定使用客製化 git 指令的方式來解決。
客製化指令其實相當簡單。
只有2個步驟,
1.建立指令檔案。
2.將指令檔案加到PATH環境變數中。


 
我的目標為自動把branch name的空格轉換為底線。e.g.
當我輸入(xxx為 git 自定義指令名稱)

git xxx branchname can not contain space

該指令會建立一個branch轉換為 branchname_can_not_contain_space,等同於輸入

git branch branchname_can_not_contain_space

 
首先
1.建立指令檔案,使用shell script來完成。
這裡要注意命名規則,指令檔案必須以 git- 開頭,後面接上你想取的名字,不需要副檔名。
以本範例來說我把它命名為 git-branchbaseline

#!/bin/bash
concat_parameters_with_baseline()
{
finalName=$@
for var in "$@"
do
finalName=${finalName/ /_}
done
echo create new branch, name:$finalName
git branch $finalName
}
concat_parameters_with_baseline $@

然後需要開放git-branchbaseline 的權限。
輸入

sudo chmod /complete/path/of/git-branchbaseline 777

 
2.考慮到之後可能會建立其他客製化指令,建立專門存放指令的資料夾在 /complete/path/of/git_customize_commands,並放入剛剛的git-branchbaseline。
將資料夾加到環境變數PATH

export PATH=$PATH:/complete/path/of/git_customize_commands

 
Update 20160622!!!
export 方式只能對目前開啟的的 Terminal 有作用,如果希望環境變數維持有效可以考慮修改 ~/.bashrc (使用bash的情況)。
因為我用的是zsh,所以需要修改 ~/.zshrc,開啟~/.zshrc 並加入

export PATH=$PATH:/complete/path/of/git_customize_commands

 
完成!!!
接著只要到 git repo 測試新指令即可。

git branchbaseline branchname can not contain space

 
 
 

分類
Uncategorized

CheckStyle 下載 編譯 執行

這裡紀錄測試後可以成功動作的部份。

使用Command Line執行

java -jar /path/of/checkstyle-x.x-all.jar -c /path/of/config.xml /path/of/files_you_want_to_check
 
ps:
以下路徑皆為完整路徑
/path/of/checkstyle-x.x-SNAPSHOT-all.jar 可以到官網下載。
/path/of/config.xml 為使用的config.xml 也可以到官網下載。
/path/of/files_you_want_to_check 為想要檢查的檔案,可以倒退到存放所有java source code 檔案位置,它會自動檢查裡面所有的檔案。


 

 

下載並編譯CheckStyle

這裡跟官網的步驟相同,需要注意的是如果想要產生checkstyle-X.X-SNAPSHOT-all.jar,必須使用mvn clean package -Passembly
Download and compile:
git clone https://github.com/checkstyle/checkstyle.git
cd checkstyle
mvn clean compile 
 
ps: mvn compile or package 無法產生checkstyle-X.X-SNAPSHOT-all.jar,只有使用mvn clean package -Passembly 才能產生


 

使用Maven 啟動 CheckStyle

Run validation with arguments:

cd /path/of/checkstyle_root_path
mvn exec:java -Dexec.mainClass=”com.puppycrawl.tools.checkstyle.Main” -Dexec.args=”-c /sun_checks.xml src/main/java ”
 

Run UI application for file :

cd /path/of/checkstyle_root_path
mvn exec:java -Dexec.mainClass=”com.puppycrawl.tools.checkstyle.gui.Main” -Dexec.args=”src/main/java/com/puppycrawl/tools/checkstyle/Checker.java”


 

Build all jars

cd /path/of/checkstyle_root_path
mvn clean package -Passembly
 
ps:會產生
/target/checkstyle-x.x-SNAPSHOT.jar
/target/checkstyle-x.x-SNAPSHOT-all.jar
/target/checkstyle-x.x-SNAPSHOT-bin.tar.gz
/target/checkstyle-x.x-SNAPSHOT-bin.zip
/target/checkstyle-x.x-SNAPSHOT-src.tar.gz
/target/checkstyle-x.x-SNAPSHOT-src.zip
/target/checkstyle-x.x-SNAPSHOT-tests.jar


 
 
 

分類
Thread

java static field synchronization in Multi-Thread

這是對在 multi-thread 中 static field 同步化的紀錄。
以下是 TestThread,在 run 函式結束後,我們希望 mNumber 不會成為負數。
重點放在第3行的 mNumber 。目前它不是 static field。

public class TestThread extends Thread
{
    private int mNumber = 100;
    public void run()
    {
        while (mNumber >= 10) {
            delayRandomMillSeconds();
            mNumber -= 10;
            System.out.println("thread name:"+getName()+" in while number:"+mNumber);
        }
        System.out.println("thread name:"+getName()+" final number:"+mNumber);
    }
    private void delayRandomMillSeconds()
    {
        try {
            Thread.sleep(new Random().nextInt(500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在非共享呼叫的情況(個別產生實體)下,mNumber不會成為負數。

    private static void runWithoutSharedResource()
    {
        new TestThread().start();
        new TestThread().start();
        new TestThread().start();
    }

Output:

thread name:Thread-2 in while number:90
thread name:Thread-0 in while number:90
thread name:Thread-1 in while number:90
thread name:Thread-2 in while number:80
thread name:Thread-1 in while number:80
thread name:Thread-0 in while number:80
thread name:Thread-0 in while number:70
thread name:Thread-1 in while number:70
thread name:Thread-2 in while number:70
thread name:Thread-2 in while number:60
thread name:Thread-2 in while number:50
thread name:Thread-2 in while number:40
thread name:Thread-0 in while number:60
thread name:Thread-0 in while number:50
thread name:Thread-1 in while number:60
thread name:Thread-1 in while number:50
thread name:Thread-2 in while number:30
thread name:Thread-0 in while number:40
thread name:Thread-1 in while number:40
thread name:Thread-2 in while number:20
thread name:Thread-0 in while number:30
thread name:Thread-2 in while number:10
thread name:Thread-1 in while number:30
thread name:Thread-0 in while number:20
thread name:Thread-2 in while number:0
thread name:Thread-2 final number:0
thread name:Thread-1 in while number:20
thread name:Thread-0 in while number:10
thread name:Thread-0 in while number:0
thread name:Thread-0 final number:0
thread name:Thread-1 in while number:10
thread name:Thread-1 in while number:0
thread name:Thread-1 final number:0

但若把 mNumber 改為 static 。

public class TestThread extends Thread
{
    private static int mNumber = 100;
    public void run()
    {
        while (mNumber >= 10) {
            delayRandomMillSeconds();
            mNumber -= 10;
            System.out.println("thread name:"+getName()+" in while number:"+mNumber);
        }
        System.out.println("thread name:"+getName()+" final number:"+mNumber);
    }
    private void delayRandomMillSeconds()
    {
        try {
            Thread.sleep(new Random().nextInt(500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

這時候 mNumber 就成為共享資源(Shared Resource)。
一樣的呼叫方式(個別產生實體)

    private static void runWithoutSharedResource()
    {
        new TestThread().start();
        new TestThread().start();
        new TestThread().start();
    }

output:

thread name:Thread-2 in while number:90
thread name:Thread-0 in while number:80
thread name:Thread-1 in while number:70
thread name:Thread-0 in while number:60
thread name:Thread-2 in while number:50
thread name:Thread-1 in while number:40
thread name:Thread-1 in while number:30
thread name:Thread-2 in while number:20
thread name:Thread-1 in while number:10
thread name:Thread-0 in while number:0
thread name:Thread-0 final number:0
thread name:Thread-1 in while number:-10
thread name:Thread-1 final number:-10
thread name:Thread-2 in while number:-20
thread name:Thread-2 final number:-20

雖然呼叫 TestThread 的方式為個別產生實體,但因為 mNumber 為 static field ,只佔一塊記憶體空間,屬於所有實體共享。
即使是不同實體也會存取到同一個mNumber,對 static field 的同步化必須特別注意才行
有三種方式可以達到 static field 的同步化。
1. static synchronized method,缺點是 static method 內無法呼叫 non-static method。

    public void run()
    {
        decreaseNumber();
    }
    private synchronized static void decreaseNumber()
    {
        while (mNumber >= 10) {
            delayRandomMillSeconds();
            mNumber -= 10;
            System.out.println("thread name:"+getName()+" in while number:"+mNumber);
        }
        System.out.println("thread name:"+getName()+" final number:"+mNumber);
    }

2. synchronized block with class object

    public void run()
    {
        synchronized(TestThread.class){
            while (mNumber >= 10) {
                delayRandomMillSeconds();
                mNumber -= 10;
                System.out.println("thread name:"+getName()+" in while number:"+mNumber);
            }
            System.out.println("thread name:"+getName()+" final number:"+mNumber);
        }
    }

3. synchronized block with lock object

    private static Object sLock = new Object();
    public void run()
    {
        synchronized(sLock){
            while (mNumber >= 10) {
                delayRandomMillSeconds();
                mNumber -= 10;
                System.out.println("thread name:"+getName()+" in while number:"+mNumber);
            }
            System.out.println("thread name:"+getName()+" final number:"+mNumber);
        }
    }

 
 
 
 

分類
Design Pattern

單例模式中資料的同步化

單例模式的基本介紹
雖然單例模式中的 HungerSingleton 可以保證只產生一個實體,但其中的資料還是需要做好同步化的動作。
首先標準的 Hunger Singleton

public class HungerSingleton
{
    private static final HungerSingleton sUniqueInstance = new HungerSingleton();
    private List<Integer> mNumbers = new ArrayList<Integer>();
    private HungerSingleton() {
        // Prevents singleton instance being instantiated from outside
    }
    public static HungerSingleton getInstance()
    {
        return sUniqueInstance;
    }
    public void addNumber(int number)
    {
        mNumbers.add(number);
    }
    public List<Integer> getNumbers()
    {
        return mNumbers;
    }
}

唯一不同的為第5行 mNumbers , 這個 List 將拿來做測試用,確保資料的同步化是否真的實現。
接著為Thread subclass

public class TestThread extends Thread
{
    @Override
    public void run()
    {
        for (int i = 0; i < 10; ++i) {
            HungerSingleton.getInstance().addNumber(i);
        }
    }
}

相當簡單,在 run 中會取得 HungerSingleton 的實體並把數值加到 mNumbers 中。
測試 Main class

public class Main
{
    public static void main(String[] args)
    {
        Thread thread = new TestThread();
        thread.start();
        Thread thread2 = new TestThread();
        thread2.start();
        try {
            Thread.currentThread().sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (Integer number : HungerSingleton.getInstance().getNumbers()) {
            System.out.println("number:" + number);
        }
    }
}

目標為讓 HungerSingleton 的 mNumbers 內容為 2 組 0 到 10 按照順序排列的數值。
執行結果

number:0
number:0
number:1
number:1
number:2
number:3
number:2
number:4
number:3
number:5
number:5
number:4
number:7
number:6
number:8
number:9
number:6
number:7
number:8
number:9

可以看到因為多執行緒的關係造成 mNumbers 內容排序錯亂。
加上同步化機制

public class TestThread extends Thread
{
    @Override
    public void run()
    {
        synchronized (HungerSingleton.getInstance()) {
            for (int i = 0; i < 100; ++i) {
                HungerSingleton.getInstance().addNumber(i);
            }
        }
    }
}

synchronized 可以讓一個執行緒取得指定物件的鎖,並擋住其他執行緒,直到該進入的執行緒完成函式內動作退出。
執行結果

number:0
number:1
number:2
number:3
number:4
number:5
number:6
number:7
number:8
number:9
number:0
number:1
number:2
number:3
number:4
number:5
number:6
number:7
number:8
number:9

從實驗結果可以看到雖然 HungerSingleton 可以保證只產生一個實體,但其內資料的操作還是需要加上同步化的動作。
 

分類
Coding Principle

撰寫變數的原則(Variable Principle)

分別從 Code Complete2 , Clean Code , The art of readable code 中擷取命名,變數的部份重點。
 
The art of readable code
1.更具體的描述(考慮名詞使用什麼,動詞使用如何來修飾)。
2.更合適的同義詞。
3.更多的意義(加入單位以及特殊意義)。
4.移除不需要的部份。
5.使用 Min and Max 代表極值。
6.使用 First and Last 代表包含值。
7.使用 Begin and End 代表包頭去尾。
8.boolean 可以使用 is , has , should , can 來修飾。
9.依照情況選擇 getXXX() , countXXX() , computeXXX()。


 
 
Code Complete2
1.盡量保持8~20字元長度。
2.問題導向,表示什麼(what)而不是如何。
3.關於計算的限定詞放到句尾,e.g. Sum , Total , Average , Max , Min。
4.小心魔術數字,可排除0和1。
5.小心除零錯誤(分母為零)
6.小心不同類型的轉換
7.小心編譯器的警告
8.狀態變數的命名,不要使用state , flag 等等看不出意義的字詞。
9.暫時變數的命名,不要使用temp , 除非真的作為temp使用。
10.布林變數的命名,常見的為 done , found , success , ok , error , 考慮使用更具體的名稱來取代以上命名。
11.列舉的命名格式,e.g.

public enum ErrorType{
DISCONNECT_ERROR , CONNECT_ERROR}

12.常數的命名,盡量不要加入具體數字。


 
Clean Code
1.名副其實(具體的描述變數與變數單位)
2.避免誤導(不要使用縮寫,不要加入型態)
3.有意義的區別(不要同時使用難以區別的名稱,不要加入不必要的字詞)
4.優先使用電腦領域術語
5.介面與實作
6.類別與方法
7.每種概念使用同一種字詞。


 
 
 

分類
Refactoring

簡化巢狀條件式的技巧(How to fix nested condition)

巢狀條件式通常為為複雜度高的區域。
以下紀錄簡化巢狀條件式的技巧


1.巢狀 if 轉換成 if then else + and。
先從最簡單的開始,e.g.

    private String nestedCondition1()
    {
        String result = "";
        if (isProcess) {
            if (isDone) {
                return result = result.concat("process is truedone is true");
            }
        }
        return result;
    }

目標是第4和第5行巢狀if,我們使用 if + and,化解巢狀if。

    private String fixByIfThenElse1()
    {
        String result = "";
        if (isProcess && isDone) {
            return result = result.concat("process is truedone is true");
        }
        return result;
    }

2.變化:原式 if 和 if 之間有statement。e.g.第5行新增 result = result.concat(“process is true”);

    private String nestedCondition2()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                return result = result.concat("done is true");
            }
        }
        return result;
    }

對於第5行有個轉換的小技巧。在2個 if 之間的 statement 可以轉換為 if(condition1 && ! condition2){statement};
需要注意的是 condition2 必須反轉。e.g.

if(condition1){
    statement1;
    if(condition2){
        statement2;
    }
}
---------trans-----------
if(condition1 && !condition2){
    statement1;
}else if(condition1 && condition2){
    statement1;
    statement2;
}

轉換 if-then-else + and

    private String fixByIfThenElse2()
    {
        String result = "";
        if(isProcess && !isDone){
            result = result.concat("process is true");
        }else if(isProcess && isDone){
            result = result.concat("process is true");
            result = result.concat("done is true");
        }
        return result;
    }

 3.變化: 原式最外層 if 產生相對應的 else。新增 9~11行

    private String nestedCondition3()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                return result = result.concat("done is true");
            }
        } else {
            result = result.concat("process is not true");
        }
        return result;
    }

轉換 if-then-else + and,新增8~10行

    private String fixByIfThenElse3()
    {
        String result = "";
        if(isProcess && !isDone){
            result = result.concat("process is true");
        }else if(isProcess && isDone){
            result = result.concat("process is true");
            result = result.concat("done is true");
        }else if(!isProcess){
            result = result.concat("process is not true");
        }
        return result;
    }

 4.變化: 原式內層 if 產生相對應 else 。新增 8~10行

    private String nestedCondition4()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                return result = result.concat("done is true");
            } else {
                return result = result.concat("done is not true");
            }
        } else {
            result = result.concat("process is not true");
        }
        return result;
    }

轉換 if-then-else + and

    private String fixAndNestedConditionSimple()
    {
        String result = "";
        if (isProcess && isDone) {
            result = result.concat("process is true");
            return result = result.concat("done is true");
        } else if (isProcess && !isDone) {
            result = result.concat("process is true");
            return result = result.concat("done is not true");
        } else if (!isProcess) {
            result = result.concat("process is not true");
        }
        return result;
    }

5.變化:外層 if 的 else 加入其他動作,12~17行

    private String nestedCondition5()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                result = result.concat("done is true");
            } else {
                result = result.concat("done is not true");
            }
        } else {
            result = result.concat("process is not true");
            if (isPrint) {
                result = result.concat("print is true");
            } else {
                result = result.concat("print is not true");
            }
        }
        return result;
    }

轉換對應的 if-then-else + and  為

private String fixByNestedCondition5()
    {
        String result = "";
        if (isProcess && isDone) {
            return result = result.concat("process is truedone is true");
        } else if (isProcess && !isDone) {
            return result = result.concat("process is truedone is not true");
        } else if (!isProcess && isPrint) {
            result = result.concat("process is not true");
            return result = result.concat("print is true");
        } else if (!isProcess && !isPrint) {
            result = result.concat("process is not true");
            return result = result.concat("print is not true");
        }
        return result;
    }

總結:
照這種排列邏輯來看基本上所有的巢狀 if 都可以轉換 if then else + and的結構,差別只在於複雜度與可讀性的不同。


 
2.以衛述句取代巢狀條件式。
這個方法是從”重構:改善既有程式的設計”一書來的。
衛述句是指條件式中若有特別的判斷邏輯,必須明確的標示出來並立刻從函式中返回。e.g.

    private String nestedCondition1()
    {
        String result = "";
        if (isProcess) {
            if (isDone) {
                return result = result.concat("process is truedone is true");
            }
        }
        return result;
    }

以衛述句改善為

    private String fixByGuardClause1()
    {
        String result = "";
        if (!isProcess) {
            return result;
        }
        if (isDone) {
            return result = result.concat("process is truedone is true");
        }
        return result;
    }

2.變化:原式 if 和 if 之間有statement。e.g.第5行新增 result = result.concat(“process is true”);

    private String fixByGuardClauseOrigin()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
        }else{
            if (isDone) {
                result = result.concat("done is true");
            }else{
                result = result.concat("done is not true");
            }
        }
        return result;
    }

以衛述句改善有2種形式,分別為是否在判斷邏輯加入 and 運算。
加入and運算後可以看到其第1種方式較為簡潔(不需要摻雜result = result.concat(“process is true”)),可讀性較高。
1.在判斷邏輯加入 and 。

    private String fixByGuardClauseWithAnd2()
    {
        String result = "";
        if(isProcess && !isDone){
            return result = result.concat("process is true");
        }
        if(isProcess && isDone){
            result = result.concat("process is true");
            return result= result.concat("done is true");
        }
        return result;
    }

2.不加入 and。

    private String fixByGuardClause2()
    {
        String result = "";
        if(!isProcess){
            return result;
        }
        result = result.concat("process is true");
        if(isDone){
            return result = result.concat("done is true");
        }
        return result;
    }

3.變化型: 原式最外層 if 產生相對應的 else。新增 9~11行

    private String nestedCondition3()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                return result = result.concat("done is true");
            }
        } else {
            result = result.concat("process is not true");
        }
        return result;
    }

以衛述句改善為

    private String fixByGuardClause3WithAnd()
    {
        String result = "";
        if(!isProcess){
            return result.concat("process is not true");
        }
        if(isProcess && !isDone){
            return result.concat("process is true");
        }
        if(isProcess && isDone){
            result = result.concat("process is true");
            return result.concat("done is true");
        }
        return result;
    }

4.變化: 原式內層 if 產生相對應 else 。新增 8~10行 

    private String nestedCondition4()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                return result = result.concat("done is true");
            } else {
                return result = result.concat("done is not true");
            }
        } else {
            result = result.concat("process is not true");
        }
        return result;
    }

以衛述句改善為

    private String fixByGuardClause4WithAnd()
    {
        String result = "";
        if(isProcess && !isDone){
            result = result.concat("process is true");
            return result = result.concat("done is not true");
        }
        if(isProcess && isDone){
            result = result.concat("process is true");
            return result = result.concat("done is true");
        }
        if(!isProcess){
            return result = result.concat("process is not true");
        }
        return result;
    }

5.變化:外層 if 的 else 加入其他動作,新增12~17行

    private String nestedCondition5()
    {
        String result = "";
        if (isProcess) {
            result = result.concat("process is true");
            if (isDone) {
                result = result.concat("done is true");
            } else {
                result = result.concat("done is not true");
            }
        } else {
            result = result.concat("process is not true");
            if (isPrint) {
                result = result.concat("print is true");
            } else {
                result = result.concat("print is not true");
            }
        }
        return result;
    }

以衛述句改善為

    private String fixByGuardClause5WithAnd()
    {
        String result = "";
        if (!isProcess && !isPrint) {
            result = result.concat("process is not true");
            result = result.concat("print is not true");
            return result;
        }
        if (!isProcess && isPrint) {
            result = result.concat("process is not true");
            result = result.concat("print is true");
            return result;
        }
        if (isProcess && isDone) {
            result = result.concat("process is true");
            result = result.concat("done is true");
            return result;
        }
        if (isProcess && !isDone) {
            result = result.concat("process is true");
            result = result.concat("done is not true");
            return result;
        }
        return result;
    }

無論使用衛述句或是if-then-else都可將難以理解的巢狀結構分解成簡單的表達式。
 
 
 
 
 

分類
Coding Principle

計算函式複雜度(by decision points – Tom McCabe)

使用 Tom McCabe 的 decision points(決策點)計算函式複雜度。
Step 1.從函式開頭以 1 開始計算。
Step 2.遇到if , while , repeat , for , and , or 類似關鍵字,加 1。
note : switch 每一個case 都要加1。
e.g.

    private void exampleCountDecisionPoints()
    {
        if((isProcess && isDone) || !isLimit) && numberLines > numberLinesLimit){
        }
    }

if +1
&& + 1
|| + 1
&& +1
total decision points = 5


 
複雜度對應原則表

0 ~ 5  -> 複雜度低可以接受
6 ~ 10 -> 複雜度中考慮簡化
10up    -> 複雜度高必須簡化

 
 

分類
Coding Principle

布林表達式(Boolean Expression)

布林表示式:
1.不要用數字(0,1)來表示布林邏輯,改用boolean。
2.使用隱式表達。e.g.

if(isDone == true) -> if(isDone)

3.可用解釋性布林變數(重構:引入解釋性變數)增加可讀性。
4.把複雜的表達式改為為布林函式(重構:分解條件式)。
5.盡量在if-else中把判斷條件轉為肯定並互換 if else的代碼。
6.使用狄摩根簡化否定的表達式。
邏輯互換表

not A and not B → not(A or B)
not A and B → not(A or not B)
A and not B → not(not A or B)
A and B → not(not A or not B)
not A or not B → not( A and B)
not A or B → not(A and not B)
A or not B → not(not A and B)
A or B → not(not A and not B)