TypeScript設計模式之職責鏈、狀態


看看用TypeScript怎樣實現常見的設計模式,順便復習一下。
學模式最重要的不是記UML,而是知道什么模式可以解決什么樣的問題,在做項目時碰到問題可以想到用哪個模式可以解決,UML忘了可以查,思想記住就好。
這里盡量用原創的,實際中能碰到的例子來說明模式的特點和用處。

職責鏈模式 Chain of Responsibility

特點:可以讓一個請求被不同的對象處理多次,請求像經過管道一樣, 一路上都可以被攔下處理。

用處:當請求需要被鏈式處理時,可以考慮職責鏈模式,比如事件的冒泡,WebApi的管道Handler等。

注意:鏈的實現。

WebApi的handler可能大家有用過,對發出去的請求和請求回來的數據都可以用自定義handler在發出前或最終回來前進行處理,非常方便,下面用TypeScript來簡單實現一個HttpHandler:

先建立一個抽象Handler類,包含一個發送請求的sendReqeust以及用來鏈式處理的innerHandler:

abstract class HttpHandler{

    constructor(protected innerHandler: HttpHandler){}

    async sendRequest(req: string): Promise<string>{
        if(this.innerHandler){
            return await this.innerHandler.sendRequest(req);
        } else {
            let res = `response`;
            console.log(res);
            return res;
        }   
    }
}

實現第一個Handler類:

class FirstHttpHandler extends HttpHandler{
    
    async sendRequest(req: string): Promise<string>{

        req = `<req1>${req}</req1>`; // 把請求包一下
        console.log(req);

        let res = await super.sendRequest(req);

        res = `<res1>${res}</res1>`; // 把結果包一下
        console.log(res);

        return res;
    }
}

再實現第二個Handler類:

class SecondHttpHandler extends HttpHandler{
    
    async sendRequest(req: string): Promise<string>{

        req = `<req2>${req}</req2>`; // 把請求包一下
        console.log(req);

        let res = await super.sendRequest(req);

        res = `<res2>${res}</res2>`; // 把結果包一下
        console.log(res);

        return res;
    }
}

把兩個HttpHandler連起來

let httpHandler = new FirstHttpHandler(new SecondHttpHandler(undefined));
console.log('start')
httpHandler.sendRequest('request').then(res=>console.log('finish'));

輸出:

start

<req1>request</req1> // 發請求前先在FirstHttpHandler里處理request

<req2><req1>request</req1></req2> // 在SecondHttpHandler里再次處理request

response // 返回數據

<res2>response</res2> // SecondHttpHandler對返回數據的第一次處理

<res1><res2>response</res2></res1> // FirstHttpHandler對返回數據的第二次處理

finish

處理的順序就是 1221,中間是真正取數據的,這就是管道處理最基本的代碼,用到的就是職責鏈模式。

當然職責鏈的形成有很多方式,這里采用的是裝飾手段,保存下一個的引用的方式來形成一個鏈表,還可以采用隊列或棧方式保存所有handler,按順序執行。

狀態模式 State

特點:通過狀態來改變對象的行為。

用處:當對象的行為取決於它的狀態或者有很多if else之類的是由狀態控制的時候可以考慮狀態模式,如常見的狀態機。

注意:狀態是由誰來轉換。

下面用TypeScript簡單實現一下狀態模式:
大家都玩過游戲,控制游戲的主角時鼠標左鍵可以是移動,遇到怪時點擊左鍵是攻擊,遇到NPC時是對話。
下面就以這點簡單實現個狀態模式:

角色和狀態的接口,狀態只需要處理當前狀態需要做的事:

interface Role{
    name: string;

    click();
    changeState(state: State);
}

interface State{
    handle(role: Role);
}

角色的具體實現:

class Player implements Role{
    private state: State;
    constructor(public name: string){
    }

    click(){
        if(this.state){
            this.state.handle(this);
        }
    }

    changeState(state: State){
        this.state = state;
        console.log(`change to ${this.state.constructor.name}`);
    }
}

狀態的具體實現,分為移動狀態,攻擊狀態,對話狀態:

class MoveState implements State{
    static readonly instance = new MoveState();

    handle(role: Role){
        console.log(`${role.name} is moving`);
    }
}

class AttackState implements State{
    static readonly instance = new AttackState();

    handle(role: Role){
        console.log(`${role.name} is attacking`);
    }
}

class TalkState implements State{
    static readonly instance = new TalkState();

    handle(role: Role){
        console.log(`${role.name} is talking`);
    }
}

使用:

let player = new Player('brook');

player.changeState(MoveState.instance);
player.click();

player.changeState(AttackState.instance);
player.click();

player.changeState(TalkState.instance);
player.click();

//輸出:
change to MoveState
brook is moving

change to AttackState
brook is attacking

change to TalkState
brook is talking

這樣隨着狀態的變化,點擊左鍵做不同的事。
對於由誰來驅動狀態變化可以根據實際情況來考慮,簡單的話直接放角色里面就行,由角色自己決定自己的狀態,復雜的話可以考慮用表來驅動狀態機,通過表過實現狀態的跳轉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM