1. 程式人生 > >設計模式讀書筆記-----職責鏈模式

設計模式讀書筆記-----職責鏈模式

       去年參加校招要到長沙來,這個對於我來說不是特別喜歡(但又必須的來,誰叫咱不是985、211的娃呢),但是對於某些人來說就是福音了。大四還有課,而且學校抓的比較嚴,所以對於那些想翹課的人來說這個是最好不過的理由了—去參加校招了。所以咱學校規定所以去參加校招的必須要請假,且必須要有相關人員的簽字,三天一下,輔導員簽字、三到七天系主任簽字,一個星期以上院長簽字,更多?校長(不知道能不能找到校長呢?反正我是沒見校長几面),出了這樣的政策確實上課情況好多了!對於這中將請求一級一級地往上傳遞直到處理請求為止的設計模式就是職責鏈模式。


上圖將學生、輔導員、系主任、院長、校長組成了一個簡單的鏈。在這個鏈上,學生是申請者,其餘的都是請求處理者。職責鏈可以將請求的處理者組織成一條鏈,並且將請求沿著鏈傳遞,如果某個請求處理者能夠處理請求則處理,否則將該請求交由上級處理。

職責鏈上的處理者負責處理請求,客戶只需要將請求傳送到職責鏈上即可,無須關心請求的處理細節和請求的傳遞,所以職責鏈將請求的傳送者和請求的處理者解耦了,這就是職責鏈的設計動機。

       一、模式定義

避免請求傳送者與接收者耦合在一起,讓多個物件都有可能接收請求,將這些物件連線成一條鏈,並且沿著這條鏈傳遞請求,直到有物件處理它為止,這就是職責鏈模式。

在職責鏈模式中最關鍵的一點就是客戶提交請求後,請求沿著鏈往下傳遞直到有一個處理者處理它,在這裡客戶無需關心它的請求是哪個處理者來處理,反正總有一個處理者會處理它的請求。

在這裡客戶端和處理者都沒有對方明確的資訊,同時處理者也不知道職責鏈中的結構。所以職責鏈可以簡化物件的相互連線,他們只需要儲存一個指向其後續者的引用,而不需要儲存所有候選者的引用。

在職責鏈模式中我們可以隨時隨地的增加或者更改一個處理者,甚至可以更改處理者的順序,增加了系統的靈活性。處理靈活性是增加了,但是有時候可能會導致一個請求無論如何也得不到處理,它會被放置在鏈末端,這個既是職責鏈的優點也是缺點。

       二、模式結構

下圖是職責鏈模式的UML結構圖:


從上面可以看出職責鏈包含三個角色:

Handler: 抽象處理者。定義了一個處理請求的方法。所有的處理者都必須實現該抽象類。 
    ConcreteHandler: 具體處理者。處理它所負責的請求,同時也可以訪問它的後繼者。如果它能夠處理該請求則處理,否則將請求傳遞到它的後繼者。 
    Client: 客戶類。

下面是最典型的具體處理者類。

public class ConcreteHandler extends Handler
{
    public void handleRequest(String request)
    {
        if(請求request滿足條件)
        {
            ......  //處理請求;
        }
        else
        {
            this.successor.handleRequest(request); //轉發請求
        }
    }
}

       三、模式的實現 

我們將使用開頭那個請假的例項。請假:3天以下輔導員簽字、3到5天系主任簽字、6到10天院長簽字、11-15天校長簽字、15天以上不允簽字。


首先是請假條:LeaveNode.java

public class LeaveNode {
    /** 請假天數 **/
    private  int number;
    
    /** 請假人 **/
    private String person;
    
    public LeaveNode(String person,int number){
        this.person = person;
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getPerson() {
        return person;
    }

    public void setPerson(String person) {
        this.person = person;
    }
}

抽象處理者:Leader.java

public abstract class Leader {
    /** 姓名 **/
    public String name;
    
    /** 後繼者 **/
    protected Leader successor;
    
    public Leader(String name){
        this.name = name;
    }

    public void setSuccessor(Leader successor) {
        this.successor = successor;
    }
    
    public abstract void handleRequest(LeaveNode LeaveNode);
}

四個具體處理者:輔導員:Instructor.java

public class Instructor extends Leader{

    public Instructor(String name){
        super(name);
    }
    
    public void handleRequest(LeaveNode LeaveNode) {
        if(LeaveNode.getNumber() <= 3){   //小於3天輔導員審批
            System.out.println("輔導員" + name + "審批" +LeaveNode.getPerson() + "同學的請假條,請假天數為" + LeaveNode.getNumber() + "天。");
        }
        else{     //否則傳遞給系主任
            if(this.successor != null){
                this.successor.handleRequest(LeaveNode);
            }
        }
    }

}

系主任: DepartmentHead.java

public class DepartmentHead extends Leader{

    public DepartmentHead(String name) {
        super(name);
    }

    public void handleRequest(LeaveNode LeaveNode) {
        if(LeaveNode.getNumber() <= 7){   //小於7天系主任審批
            System.out.println("系主任" + name + "審批" +LeaveNode.getPerson() + "同學的請假條,請假天數為" + LeaveNode.getNumber() + "天。");
        }
        else{     //否則傳遞給院長
            if(this.successor != null){
                this.successor.handleRequest(LeaveNode);
            }
        }
    }
}

院長:Dean.java

public class Dean extends Leader{

    public Dean(String name) {
        super(name);
    }

    public void handleRequest(LeaveNode LeaveNode) {
        if(LeaveNode.getNumber() <= 10){   //小於10天院長審批
            System.out.println("院長" + name + "審批" +LeaveNode.getPerson() + "同學的請假條,請假天數為" + LeaveNode.getNumber() + "天。");
        }
        else{     //否則傳遞給校長
            if(this.successor != null){
                this.successor.handleRequest(LeaveNode);
            }
        }
    }

}

校長:President.java

public class President extends Leader{

    public President(String name) {
        super(name);
    }

    public void handleRequest(LeaveNode LeaveNode) {
        if(LeaveNode.getNumber() <= 15){   //小於15天校長長審批
            System.out.println("校長" + name + "審批" +LeaveNode.getPerson() + "同學的請假條,請假天數為" + LeaveNode.getNumber() + "天。");
        }
        else{     //否則不允批准
            System.out.println("請假天天超過15天,不批准...");
        }
    }

}

客戶端:Client.java

public class Client {
    public static void main(String[] args) {
        Leader instructor = new Instructor("陳毅");       //輔導員
        Leader departmentHead = new DepartmentHead("王明");    //系主任
        Leader dean = new Dean("張強");      //院長
        Leader president = new President("王晗");     //校長
        
        instructor.setSuccessor(departmentHead);       //輔導員的後續者是系主任
        departmentHead.setSuccessor(dean);             //系主任的後續者是院長
        dean.setSuccessor(president);                  //院長的後續者是校長
        
        //請假3天的請假條
        LeaveNode leaveNode1 = new LeaveNode("張三", 3);
        instructor.handleRequest(leaveNode1);     
        
        //請假9天的請假條
        LeaveNode leaveNode2 = new LeaveNode("李四", 9);
        instructor.handleRequest(leaveNode2);
        
        //請假15天的請假條
        LeaveNode leaveNode3 = new LeaveNode("王五", 15);
        instructor.handleRequest(leaveNode3);
        
        //請假20天的請假條
        LeaveNode leaveNode4 = new LeaveNode("趙六", 20);
        instructor.handleRequest(leaveNode4);
    }
}

執行結果:


       四、模式的優缺點 

優點

1、降低耦合度。它將請求的傳送者和接受者解耦。

2、簡化了物件。使得物件不需要知道鏈的結構。

3、增強給物件指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。

4、增加新的請求處理類很方便。

缺點

1、不能保證請求一定被接收。

2、系統性能將受到一定影響,而且在進行程式碼除錯時不太方便;可能會造成迴圈呼叫。

3、可能不容易觀察執行時的特徵,有礙於除錯。

       五、模式適用場景

1、有多個物件可以處理同一個請求,具體哪個物件處理該請求由執行時刻自動確定。

2、在不明確指定接收者的情況下,向多個物件中的一個提交一個請求。

3、可動態指定一組物件處理請求。

       六、總結

1、職責鏈模式將請求的傳送者和接受者解耦了。客戶端不需要知道請求處理者的明確資訊,甚至不需要知道鏈的結構,它只需要將請求進行傳送即可。

2、職責鏈模式能夠非常方便的動態增加新職責或者刪除職責。

3、客戶端傳送的請求可能會得不到處理。

4、處理者不需要知道鏈的結構,只需要明白他的後續者是誰就可以了。這樣就簡化了系統中的物件。