JavaScript 設計模式 --- 策略模式

前言

在我們編寫業務代碼時,分支結構應該是我們最常用到的結構了。下面先模擬一個簡單的業務場景:
要根據當前用戶登錄的身份(達人/機構/賣家)和對應的角色等級來展示用戶的logo

// 假設有這麼一個基於React的logo組件
<logo role={role} level={level} />

// 假如內部的實現是這樣子的
render(){
 	const { role, level } = this.props;
    if (role === '達人'){
        return <span className={a}>{level}</span>
    } else if(role === '機構'){
        return <span className={b}>{level}</span>
    } else if(role === '賣家'){
        return <span className={c}>{level}</span>
    }
}

不需要在意上述代碼的細節,假設身份最後會映射到一個class類名上,並且等級的展示也會依賴於用戶的身份而有所不同。這裏的關鍵主要還是如何去抽離出這種複雜的業務邏輯,我們希望這段邏輯可以被複用,而不是耦合在這個組件的實現裏面。

策略模式的定義

我們需要定義一個策略類(strategy),每一個類都封裝了一個算法用於解決相同的問題。還需要一個上下文(context)來供外部調用,傳入相應的參數。

先嚐試用面向對象的思維來解決問題

看一下這段代碼

// 達人策略類
function InfluencerStrategy(){
}
InfluencerStrategy.prototype.getDisplayClassName = function(role){
    // 一段很複雜的邏輯之後
    return somethingComplex(role)
}
InfluencerStrategy.prototype.getDisplayLevel = function(level){
    // 一段很複雜的邏輯之後
    return somethingComplex(level)
}

// 機構策略類
function OrganizationStrategy(){

}
OrganizationStrategy.prototype.getDisplayClassName = function(role){
    // 一段很複雜的邏輯之後
    return somethingComplex(role)
}

OrganizationStrategy.prototype.getDisplayLevel = function(level){
    // 一段很複雜的邏輯之後
    return somethingComplex(level)
}

//賣家策略類
function SellerStrategy(){

}
SellerStrategy.prototype.getDisplayClassName = function(role){
    // 一段很複雜的邏輯之後
    return somethingComplex(role)
}
SellerStrategy.prototype.getDisplayLevel = function(level){
    // 一段很複雜的邏輯之後
    return somethingComplex(level)
}

function Context(role, level){
    this.role = role;
    this.level = level;
    this.roleStrategy = null;
}
Context.prototype.setRole= function(role){
    this.role = role;
}
Context.prototype.setLevel = function(level){
    this.level = level;
}
Context.prototype.setRoleStrategy = function(rs){
    this.roleStrategy = rs;
}
Context.prototype.getClass = function(){
    return this.roleStrategy.getDisplayClassName(this.role);
}
Context.prototype.getLevel = function(){
    return this.roleStrategy.getDisplayLevel(this.level);
}

const context = new Context('某個角色名稱','某個等級');
context.setRoleStrategy(new SellerStrategy());
context.getClass();
context.getLevel();

context.setRole('其他的角色名稱');
context.setLevel('其他的等級');
context.setRoleStrategy(new OrganizationStrategy());
context.getClass();
context.getLevel();

上述代碼看起來還是蠻多的,其實邏輯十分簡單。我們這裏主要是關注Context上的getClass

Context.prototype.getClass = function(){
    return this.roleStrategy.getDisplayClassName(this.role);
}

可以看到他並不負責具體的算法實現,而是把這個需求委託給了內部的一個策略實例,而這裏所有的策略實例都實現了getDisplayClassName這個接口。
也就是說,策略模式講究的是算法的實現和使用是分離的,實際上上面的幾個策略類完全可以導出給外部使用,這樣複用性能得到很大提升。

也許函數更好

面向對象的思路確提供了一個可行的解決方案,但是在JavaScript中,其實一個簡單的函數就可以封裝複雜的算法,也可以導出方便外部使用,更重要的是,方便單元測試。
我們改造一下代碼

const roleStrategyMap = {
    'influencer':{
        getClass:function(role){

        },
        getLevel:function(level){

        }
    },
    'organization':{
        getClass:function(role){

        },
        getLevel:function(level){

        }
    },
    'seller':{
        getClass:function(role){

        },
        getLevel:function(level){

        }
    },
};

在調用的時候只要這樣

roleStrategyMap[role].getClass()

roleStrategyMap在這裏就是承擔context的作用,通過role最後找到委託的策略對象,調用指定的方法。

寫在最後

其實這種模式在開發過程中被用到的頻率十分的高,簡而言之,對於所有複雜的分支結構理論上都可以利用策略模式來重新改寫,但是也不能以一概全,原生的if和else也許更助於人理解業務邏輯,在實際處理過程中也要注意取捨。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章