分類
zsh

How to setup autojump with zsh

1. git clone autojump

git clone https://github.com/joelthelion/autojump.git

after clone, it will create a dir named autojump
 

2. install autojump

cd autojump
./install.sh

after install autojump, it will create config file of autojump at ~/.autojump
 

3. setup config file for autojump

3-1. vim ~/.zshrc and add autojump at plugins=(xxx autojump)

...
# Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*)
# Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/
# Example format: plugins=(rails git textmate ruby lighthouse)
# Add wisely, as too many plugins slow down shell startup.
plugins=(git autojump)
...

3-2 check shell file name of autojump is correct e.g. ~/.autojump/etc/profile.d/autojump.sh or ~/.autojump/etc/profile.d/autojump.zsh
add shell file of autojump file in ~/.zshrc

...
[[ -s ~/.autojump/etc/profile.d/autojump.zsh ]] && . ~/.autojump/etc/profile.d/autojump.zsh

 

4. reload config file

source ~/.zshrc

 

5. Now Jump it!!

 

分類
zsh

How to install zsh(zshell) and oh-my-zsh on Ubuntu

1.Install zsh

$ sudo apt-get install zsh

2.Install oh-my-zsh

$ wget --no-check-certificate https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh

若發生以下錯誤訊息代表目前電腦不信任 git 伺服器憑證。該憑證是自行簽署的或是其他未知的機構簽署。

fatal: unable to access 'https://github.com/ohmyzsh/ohmyzsh.git/': server certificate verification failed. CAfile: none CRLfile: none
Error: git clone of oh-my-zsh repo failed

可以使用以下指令暫時忽略憑證

export GIT_SSL_NO_VERIFY=1

3.Now you can modify config file of zsh at

~/.zshrc

 

 

分類
git 使用紀錄

Git : 無法加入檔案( Can't git add file)

Description : 

當在git project 底下,若有些檔案無法加入(git add),很有可能是被 git ignore了。
詳細說明參考這篇(http://stackoverflow.com/questions/1084969/unable-to-track-files-within-git-submodules)
以下節錄相關的說明
大部分的情況下,檔案被 git ignore,有2個主要原因:.gitignore submodule
.gitignore 是一個說明檔和 .git 位於同一層,主要的用途就是忽略不需要追蹤的檔案。
android project 為例,當進行clean build 的動作都會讓bin/ 資料夾內的檔案重新生成。
這些重新生成的檔案多半是不需要追蹤,因此就能藉由 .gitignore 說明檔來描述哪些檔案不需要追蹤。
以下為簡單的範例
這是剛建立完成android project並初始化 git(git init)的情況。可以看到 bin/ 資料夾。

# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	.classpath
#	.project
#	AndroidManifest.xml
#	bin/
#	gen/
#	ic_launcher-web.png
#	libs/
#	proguard-project.txt
#	project.properties
#	res/
#	src/

接著建立 .gitignore 檔案。
vim .gitignore 並填入 bin/ bin/ 的意思為忽略 bin/ 內所有檔案)
再輸入 git status 觀察,可以看到多了.gitignore ,少了bin/

# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	.classpath
#	.gitignore
#	.project
#	AndroidManifest.xml
#	gen/
#	ic_launcher-web.png
#	libs/
#	proguard-project.txt
#	project.properties
#	res/
#	src/

此即為第一種 git ignore 的情況,若直接輸入 git add bin/ git 會說明情況

The following paths are ignored by one of your .gitignore files:
bin
Use -f if you really want to add them.
fatal: no files added

強制加入請輸入 git add –force bin/

# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#	new file:   bin/AndroidManifest.xml
#	new file:   bin/R.txt
#	new file:   bin/classes/android/support/v7/appcompat/R$anim.class
#	new file:   bin/classes/android/support/v7/appcompat/R$attr.class
#	new file:   bin/classes/android/support/v7/appcompat/R$bool.class
#	new file:   bin/classes/android/support/v7/appcompat/R$color.class
#	new file:   bin/classes/android/support/v7/appcompat/R$dimen.class
#	new file:   bin/classes/android/support/v7/appcompat/R$drawable.class
#	new file:   bin/classes/android/support/v7/appcompat/R$id.class
...

 
2種情況為 submodule
這種情況比較複雜一些,必須進行4個步驟。
Note : 注意這4個步驟可能會毀損 git repo,若不確定情況,可以先試試第3步驟。
1. .gitmodule中刪除相關的連結
2. .git/config中刪除相關的連結
3. 執行 git rm –cached path_to_submodule

rm 'path_to_submodule'

4. 執行 git commit

a[master 5c270a9] [xxxx] Remove file in submodule.
 1 file changed, 1 deletion(-)
 delete mode 160000 path_to_submodule

 
 
 

分類
git 使用紀錄

Git : 如何取消已經追蹤的檔案(set file to untracked)

Description:
android project 為例,大多數的情況下,不需要追蹤 /bin 資料夾裡的檔案。但若已經將bin資料夾加入追蹤。要如何讓 bin 回到 untrack 的狀態?
 
Solution:
1.移動到 project 根目錄下輸入
git rm –cached -r bin

rm 'bin/AndroidManifest.xml'
rm 'bin/R.txt'
rm 'bin/classes/android/support/v7/appcompat/R$anim.class'
rm 'bin/classes/android/support/v7/appcompat/R$attr.class'
rm 'bin/classes/android/support/v7/appcompat/R$bool.class'
rm 'bin/classes/android/support/v7/appcompat/R$color.class'
rm 'bin/classes/android/support/v7/appcompat/R$dimen.class'
rm 'bin/classes/android/support/v7/appcompat/R$drawable.class'
rm 'bin/classes/android/support/v7/appcompat/R$id.class'
rm 'bin/classes/android/support/v7/appcompat/R$integer.class'
rm 'bin/classes/android/support/v7/appcompat/R$layout.class'
rm 'bin/classes/android/support/v7/appcompat/R$string.class'
rm 'bin/classes/android/support/v7/appcompat/R$style.class'
...

 
2.
commit 該動作。(git commit)

[master 846ecc5] [Test] Ignore all files in /bin folder.
 31 files changed, 620 deletions(-)
 delete mode 100644 bin/AndroidManifest.xml
 delete mode 100644 bin/R.txt
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$anim.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$attr.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$bool.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$color.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$dimen.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$drawable.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$id.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$integer.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$layout.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$string.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$style.class
 delete mode 100644 bin/classes/android/support/v7/appcompat/R$styleable.class
...

 
3.
使用 git status 查詢,可以看到 bin 資料夾已經回到 untracked 狀態。

# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	bin/
nothing added to commit but untracked files present (use "git add" to track)

 

分類
Design Principle

Law of Demeter (LOD) (迪米特法則)

Law of Demeter (LOD)

定義:

也稱最少知識原則(Principle of Least Knowledge)。
模組應該儘可能的減少其他模組交互,目的在於降低彼此之間的依賴。
 

說明:

以下為必須遵循 LOD 的條件
類別 O 的任何方法 m 只能呼叫屬於以下情況的方法
1. 類別 O 本身的方法。
2. 傳入 m 的參數的方法。
3. 在 m 中建立對象的方法。
4. 任何直接持有的對象方法。
 
Example Code :

public class Man {
    private Vehicle mVehicle;
    private void methodInMan(){}
    public void driveVehicle(Vehicle vehicle){
    	methodInMan();// Rule 1
        vehicle.drive();// Rule 2
        new Car().drive();// Rule 3
        mVehicle = new Bike();
        mVehicle.drive();//Rule 4
        new Bike().getClass().getName(); //violation at getName()
    }
}

如何修改遵循 LOD ,可以在 Bike 中直接建立方法來取得 class name,如下

public class Bike implements Vehicle{
    ...
    public String getClassName(){
        return getClass().getName();
    }
}

client 端呼叫

public class Man {
    private Vehicle mVehicle;
    private void methodInMan(){}
    public void driveVehicle(Vehicle vehicle){
    	methodInMan();// Rule 1
        vehicle.drive();// Rule 2
        new Car().drive();// Rule 3
        mVehicle = new Bike();
        mVehicle.drive();//Rule 4
        new Bike().getClassName(); //Rule 3
    }
}

 
注意:
在重構中的程式碼壞味道第15點 Message Chain (過度耦合的消息鏈) 其本身也是描述相同的問題:
一個對象請求另一個對象,然後再向後者請求一個對象。
代表對象之間的結構緊密耦合,一旦對象關係發生變化,客戶端不得不做出相應修改。
可以使用 Hide Delegate 來重構!!
 

分類
Design Principle

Interface Segregation Principle (ISP) (介面隔離原則)

Interface Segregation Principle (ISP)

 

定義:

客戶端不應該被強迫依賴不需要的方法。(介面應該只提供客戶端需要的方法)
 
 

說明:

一個過大的 interface ,通常代表其中有某些功能是客戶端不需要的,如果客戶端實作了不需要的功能 ,這些功能會造成不必要的耦合
我們可以把過大的 interface 分離,將其中某些功能拆離到另一個 interface 中。
 

Example Code:

回到 OCP 的初始範例,其中 Bike , Car 都有類似的方法( driveBike , driveCar)。

public class Bike {
    public void driveBike(){
        System.out.println("drive Bike");
    }
}
public class Car {
    public void driveCar(){
        System.out.println("drive Car");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

現在必須建立抽象層 Vehicle,和 OCP 不同的是我們把 openWindow 也放到 Vehicle 中。

public interface Vehicle {
    public void drive();
    public void openWindow();
}

對 Bike 而言,必須實作 openWindow 行為,但該行為對 Bike 是沒有意義,也違反了 ISP。

public class Bike implements Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
    @Override
    public void openWindow() {
        // unmeaning action for Bike
    }
}

為了遵循 ISP,可以另外建立 Window 介面,並把 openWindow 移到 Window 介面中。

public interface Window {
    public void openWindow();
}

現在 Bike 可以不必實作 openWindow 方法。

public class Bike implements Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
}

 
 
 

分類
Design Principle

Dependence Inversion Principle (DIP) (相依反向原則)

Dependence Inversion Principle (DIP)

 

定義:

高層模組不應該相依於低層模組,兩者都應該相依於抽象。
抽象不應該相依於細節,細節應該相依於抽象。
 

說明:

傳統的軟體開發方法,如結構化分析與設計傾向於創造高階模組相依於低階模組。目標就是描述高階模組如何呼叫低階模組的子程式結構
相較於以傳統程序化方法的相依結構,設計良好的物件導向程式的相依結構應該是反向的
高層指的是依賴者 , 低層指的是被依賴者。如下圖
dip_before
模組之間的依賴應該透過抽象,而不應該透過具體的方式(針對介面寫程式)
對象的引用盡量是抽象型態而不是具體型態
因此使用DIP之後應該為
dip_after
HighLevel 和 LowLevel 都相依於 abstract level。
 

注意:

若是具體類別已經相當穩定,不太會變化,依賴於該具體類別也是無妨
 
Example Code:
以先前的 LSP 的範例 來說明

public interface Vehicle {
    public void drive();
}
public class Bike implements Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
}
public class Car implements Vehicle{
    public void drive(){
        System.out.println("drive car");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

若用一個 Man 來代表使用者,並讓 Man 可以操縱 Bike,在未加考慮的情況會直接寫出以下 code,並違反了 DIP。
在 driveBike 的方法參數,並沒有依賴抽象(Vehicle),相反的依賴具體型態(Bike)。

public class Man {
    public void driveBike(Bike bike){
        bike.drive();
    }
}

修改的方法很簡單,把參數型態改為 Vehicle。
另一個依賴抽象的好處是可以減少多餘的code,在依賴具體 Bike 的情況下, 若我們想讓 Man 可以駕駛 Car,  必須還要寫出另一個 driveCar 方法。

public class Man {
    public void driveBike(Bike bike){
        bike.drive();
    }
    public void driveCar(Car car){
        car.drive();
    }
}

修改 Man 的 driveVehicle 方法,讓其參數型態依賴抽象(Vehicle)。

public class Man {
    public void driveVehicle(Vehicle vehicle){
        vehicle.drive();
    }
}

調用端呼叫 driveVehicle 方法可以根據傳入參數型態的不同,具有不同的行為。

public class Main {
    public static void main(String[] args) {
        Man man = new Man();
        man.driveVehicle(new Bike());
        man.driveVehicle(new Car());
    }
}

 
另一種依賴具體的形式為基本型別。如

public int getSummary(int a, int b)
{
...
}

如果需要支援多種 Summary 算法,可以引入抽象層為參數。

public int getSummary(CustomType a, CustomType b){
...
}
public interface CustomType{
...
}

 
 
 
 
 
 
 

分類
Design Principle

Liskov Substitution Principle (LSP) (Liskov 替換原則)

Liskov Substitution Principle (LSP)

 

定義:

衍生類別(Sub Type)必須能夠替換成他們的基礎類別(Base Type)。
衍生類別應該可以替換任何基楚類別出現的位置,且程序還能正常工作。
 

說明:

1. 不能僅用 is-a 的關係就建立繼承,必須考慮是否在基礎類別中有些方法對衍生類別而言是不需要或是無意義的。
這些沒有意義的方法會造成不可預期的結果。
2.一些違反 LSP 觀察點(基本上從衍生類別刪除基礎類別的功能就代表了無法取代基礎類別,因而違反了LSP)
1.退化函式(不一定絕對違反LSP,但需要檢查一下),基本上退化函式就是繼承基礎類別的函式,但在該函式中沒有做任何事,這種函式就是沒有意義的。

public class BaseClass {
  public void method(){
    //do something
  }
}
public class DeriveClass extends BaseClass{
  @Override
  public void method() {
    //do nothing
  }
}

DeriveClass 的 method 可能違反了LSP
2.在衍生類別丟出基礎類別沒有的異常

注意:

必須要有繼承關係才需要考慮 LSP ,而 LSP 是讓設計達到 OCP 的規則之一 。
 

Example Code :

Vehicle 有2個簡單的方法, 分別是 drive (駕駛)和 openWindow (打開車窗)。

public class Vehicle {
    public void drive(){
        System.out.println("drive vehicle");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

若單以語意而言 Vehicle 和 Bike  有 is-a 的關係,也有共同的方法 drive , 但 openWindow 對 Bike 而言是沒有意義的。
若強制讓 Bike 繼承 Vehicle 會有2種情況。
1. Bike 不覆寫 openWindow 方法

public class Bike extends Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
}

2. Bike 覆寫 openWindow 方法,但方法內容令人迷惑。

public class Bike extends Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
    public void openWindow(){
        System.out.println("Bike can't open window");
    }
}

不論哪種方法都無法到達呼叫端預期的結果 e.g.

Vehicle bike = new Bike();
bike.openWindow();// why bike can open window?

因此 Bike 繼承 Vehicle 不符合 LSP 。
 
如何讓 Bike 符合 LSP?
我們可以重新建立抽象層 Vehicle 並提取 Bike 和 Car 共同的部份並放入 Vehicle 中,並讓2者都派生 Vehicle。

public interface Vehicle {
    public void drive();
}

Bike 實作 Vehicle

public class Bike implements Vehicle{
    public void drive(){
        System.out.println("drive Bike");
    }
}

Car 實作 Vehicle

public class Car implements Vehicle{
    public void drive(){
        System.out.println("drive car");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

最後以語意來說 Car , Bike 都是 Vehicle。 Vehicle 的方法對於 Car , Bike 都是有意義的。
 

分類
Design Principle

Open Close Principle (OCP) (開放關閉原則)

Open-Close Principle(OCP) 

 

定義:

軟體實體(Class, Method, Module等等)應該針對擴展開放,針對修改關閉
換句話說應該盡量透過擴展的方式來實現變化,而不是透過修改原架構的方式。
 

說明:

OCP 的2個特徵:
1.針對擴展而開放:
當需求發生變化時,必須可以改變模組以符合需求的變化。
2.針對修改而關閉:
當改變模組時,不必改動原有的架構。
ㄧ開始看起來1跟2是互相矛盾的,一般來說改變模組就是代表修改架構,但在OOP中可以藉由抽象來達到。
如何利用抽象來達到OCP呢?
模組依賴於固定的抽象型別,就是代表面對修改而關閉,讓需求發生變化的部份實現在該抽象型別的子類別中,就是面對擴展而開放。
對有相似行為類別的建立抽象層,如 abstract class, 或是 interface
將公共屬性或方法提取到抽象層中,當需要擴展行為(新增)時只需要建立新的子類別並繼承抽象層,不必修改原有的行為
 

注意:

實作 OCP 抽象層需要花費時間和精力,也可能會造成複雜度的上升,OCP 應該只運用在程序中頻繁發生的變化上
在敏捷軟件開發:原則,樣式及實務(Agile software development: principles, Patterns, and Practices)中提到
可以運用 “第一顆子彈” 來說明運用時機。
第一顆子彈:最初編寫代碼時假設變化不會發生,因此不做出用不到的抽象層,直到變化發生,再加入抽象層隔離同類變化。

 

Example Code :

Car , Bike 都有類似的方法(driveBike , driveCar)但並沒有為其建立抽象層。

public class Bike {
    public void driveBike(){
        System.out.println("drive Bike");
    }
}
public class Car {
    public void driveCar(){
        System.out.println("drive Car");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

 
Man 類別會依賴於 Bike 和 Car 類別。

public class Man {
    private List mVehicle = new LinkedList();
    public Man() {
        mVehicle.add(new Bike());
        mVehicle.add(new Car());
    }
    public void driveVehicle() {
        for (int i = 0; i < mVehicle.size(); ++i) {
            Object object = mVehicle.get(i);
            if( object instanceof Car){
                ((Car) object).driveCar();
            }else if( object instanceof Bike){
                ((Bike) object).driveBike();
            }
        }
    }
}

注意第10行的 driveVehicle 方法,因為必須判斷各物件的型態,所以不得不加入長串的if-else判斷式。
若之後新增 Boat 類別,就必須修改 driveVehicle 方法並加入 object instanceof Boat 判斷式,造成不符合 OCP 設計
(思考的方向為當出現第N種車輛時,對於Man類別來說要如何設計,以達到不需要修改Man的架構,就可以加入新的車輛。)
 
 
如何修改為符合 OCP 設計?
首先建立介面 Vehicle , 並把公共方法提取到抽象層中。

public interface Vehicle {
    public void drive();
}

接著 Car , Bike 實作 Vehicle 介面。

public class Bike implements Vehicle{
    @Override
    public void drive() {
        System.out.println("drive Bike");
    }
}
public class Car implements Vehicle{
    @Override
    public void drive() {
        System.out.println("drive Car");
    }
    public void openWindow(){
        System.out.println("open window");
    }
}

 
在 driveVehicle 方法中不必判斷各物件的型別, 之後即使新增任何交通工具,只要讓新增的類別實作 Vehicle 介面 ,driveVehicle 都不需要改變。

public class Man {
    private List<Vehicle> mVehicle = new LinkedList<Vehicle>();
    public Man() {
        mVehicle.add(new Bike());
        mVehicle.add(new Car());
    }
    public void driveVehicle() {
        for (int i = 0; i < mVehicle.size(); ++i) {
            mVehicle.get(i).drive();
        }
    }
}

以上使用多型的方式去除型別判斷(instanceof)的條件式達到OCP的作法。
 
事實上最常見違反OCP的是另一種根據布林變數來判斷行為的條件式。 e.g.

        if(conditionA){
            statementA;
        }else if(conditionB){
            statementB;
        }else if(conditionC){
            statementC;
        }

 
這種案例的修改必須借助多型,但還需要加上相依性注入(Dependency Injection)
範例如下
假設有個紅綠燈系統,參與的類別各為 GreenLight.java , RedLight.java , YellowLight.java , 以及 LightActionManager.java,如下

public class GreenLight
{
    public void lightOn()
    {
        System.out.println("green light action");
    }
}
public class RedLight
{
    public void lightOn()
    {
        System.out.println("red light action");
    }
}
public class YellowLight
{
    public void lightOn()
    {
        System.out.println("yellow light action");
    }
}

以及 LightActionManager.java

public class LightActionManager
{
    private boolean mIsGreenLightOn;
    private boolean mIsYellowLightOn;
    private boolean mIsRedLightOn;
    private GreenLight mGreenLight = new GreenLight();
    private YellowLight mYellowLight = new YellowLight();
    private RedLight mRedLight = new RedLight();
    public LightActionManager(boolean greenLightOn, boolean redLightOn, boolean yellowLightOn) {
        mIsGreenLightOn = greenLightOn;
        mIsYellowLightOn = yellowLightOn;
        mIsRedLightOn = redLightOn;
    }
    public void lightAction()
    {
        if (mIsGreenLightOn) {
            mGreenLight.lightOn();
        } else if (mIsYellowLightOn) {
            mYellowLight.lightOn();
        } else if (mIsRedLightOn) {
            mRedLight.lightOn();
        }
    }
}

違反OCP的位置在於第17行的 lightAction 函式中使用多個邏輯判斷。
假設之後需要加入綠燈左轉燈號(GreenLightLeft.java),綠燈右轉燈號(GreenLightRight.java)就必須修改if-else判斷 e.g.

    public void lightAction()
    {
        if (mIsGreenLightOn) {
            mGreenLight.lightOn();
        } else if (mIsYellowLightOn) {
            mYellowLight.lightOn();
        } else if (mIsRedLightOn) {
            mRedLight.lightOn();
        } else if (mIsGreenLightLeftOn) {
            mGreenLightLeft.lightOn();
        } else if (mIsGreenLightRightOn) {
            mGreenLightRight.lightOn();
        }
    }

 
如何藉由多型以及相依性注入來改善呢?
首先建立interface (ILight)來達到多型,並讓 GreenLight.java , RedLight.java , YellowLIght.java 實作。

public interface ILight
{
    public void lightOn();
}
public class GreenLight implements ILight
{
    public void lightOn()
    {
        System.out.println("green light action");
    }
}
public class YellowLight implements ILight
{
    public void lightOn()
    {
        System.out.println("yellow light action");
    }
}
public class RedLight implements ILight
{
    public void lightOn()
    {
        System.out.println("red light action");
    }
}

接著修改LightActionManager.java,如下

public class LightActionManager
{
    private ILight mLight;
    public LightActionManager(ILight light) {
        mLight = light;
    }
    public void setLight(ILight light){
        mLight = light;
    }
    public void lightAction()
    {
        if(mLight != null){
            mLight.lightOn();
        }
    }
}

第4行需要持有1個ILight 型別的欄位。
第6行建立LightActionManager的時候就必須傳入Light實體。
第10行即為相依性注入的函式,這個部份相當重要,它是取代條件判斷式的一個重點。
第14行為提供給外部呼叫的函式。
接著來看看外部如何呼叫LightActionManager.java

        LightActionManager actionManager = new LightActionManager(new GreenLight());
        actionManager.lightAction();
        actionManager.setLight(new YellowLight());
        actionManager.lightAction();

我們藉由相依性注入函式(setLight())設定不同的Light,接著才呼叫lightAction()。
另一個好處是藉由提供ILight interface , 可以讓其他開發者實作自己的燈號類別(XXXLight),只要記得實作ILight(implements ILight)即可。
但缺點是引入一層抽象層(ILight),即使抽象層內容簡單,還是需要時間以及人力維護。
所以應該在面對系統中最頻繁出現的變化位置再考慮使用多型以及使用相依性注入的解決方案。
 
 
 
 
 

分類
Design Principle

Single Responsibility Principle (SRP) (單一責任原則)

Single Responsibility Principle (SRP)

 

定義:

對一個類別而言,應該僅有一個引起它變化的原因(職責)
 

說明:

若一個類別有多重職責,職責之間會互相耦合,一個職責的變化可能會影響該類別完成其他職責的能力
注意:運用本原則以及其他設計原則必須在實際發生的情況下才使用,不要預先做目前不需要的設計。
 

1. 以變化的參與性決定。

若需求的出現導致類別中所有部份發生變化,代表所有職責可視為同一個職責,不必分離。如果分離反而會導致不必要的複雜度
相反的,若需求的出現導致類別中某一部份(欄位或函式)發生變化,代表某一部份為不同職責,必須分離,否則會造成僵化性。
 

2. 以變化的頻率和因素決定

變化頻率而言,商業邏輯經常變化,資料儲存變化較少。
變化因素而言,商業邏輯和資料儲存的變化因素多不相同。
所以商業邏輯和資料儲存不應該同屬於一個類別
 

3. 以重構(Refactoring)來分離多重職責

在重構中程式碼的壞味道(bad smell)中,第3點過大的類別(Large Class),以及第5點發散式變化(Divergent Change)都是關於多重職責的問題。
可使用 Extract Class,Extract Subclass 來切割類別以分離多重職責。
 

4. 在無暇的程式碼(Clean Code)提到,類別若具有多重職責代表該類別內聚力低,為了保持類別的高內聚力,

可以拆解內容過長的方法並觀察拆解後的方法是否具有變化的參與性,若變化總是在某些方法中出現,就把它們提取出來吧。