重構-以多態取代條件表達式
復雜的條件邏輯是編程中最難理解的東西之一,給條件邏輯添加結構。
可以將條件邏輯拆分到不同的場景(或者叫高階用例),從而拆分條件邏輯。使用類和多態能把邏輯的拆分表述得更清楚,多態是改善復雜條件邏輯的有力工具。
有兩種常見場景,
一種是,好幾個函數都有基于類型的switch語句,每個類型處理各自的條件邏輯。把switch中每個分支邏輯創建一個類,用多態來承載各個類型特有的行為。
另一種是:有一個基礎邏輯,在其上又有一些變體。基礎邏輯放進基類,基礎邏輯可能是最常用的,也可能是最簡單的。變體放進子類,強調與基類基礎邏輯的差異。
一、改造switch
1,動機
例子,朋友有一群鳥,他想知道鳥飛得多快,以及鳥的羽毛是什么樣的。
//朋友有一群鳥,他想知道鳥飛得有多快,以及它們的羽毛是什么樣的。 //飛的多快 function speeds(birds) { return new Map(birds.map(b => [b.name, airSpeedVelocity(b)])); } //羽毛 function plumages(birds) { return new Map(birds.map(b => [b.name, plumage(b)])) } function plumage(bird){ switch(bird.type){ case 'EuropeanSwallow': return "average"; case 'AfricanSwallow': return (bird.numberOfCoconuts>2)?"tired":"average"; case 'NorwegianBlueParrot': return (bird.voltage>100)?'scorched':'beautiful'; default: return 'unknow'; } } function airSpeedVelocity(bird){ switch(bird.type){ case 'EuropeanSwallow': return 35; case 'AfricanSwallow': return 40-2*bird.numberOfCoconuts; case 'NorwegianBlueParrot': return (bird.isNailed)?0:10+bird.voltage/10; default: return null; } }
2,做法
1,如果現有的類不具備多態行為,就用工廠函數創建之,令工廠函數返回恰當的對象實例。
2,在調用方代碼中使用工廠函數獲得對象實例。
3,將帶有條件邏輯的函數移到超類中。
如果條件邏輯還未提煉至獨立的函數,首先對其使用提煉函數。
4,任選一個子類,在其中建立一個函數,使之覆寫超類中容納條件表達式的那個函數。將與該子類相關的條件表達式分支復制到新函數中,并對它進行適當調整。
5,重復上述過程,處理其他條件分支。
6,在超類中保留默認情況的邏輯。或者,如果超類應該是抽象的,就把該函數聲明為abstract,或在其中直接拋出異常,表明計算責任都在子類中。
3,實例
對airSpeedVelocity和plumage兩個函數使用函數組合成類,這個類會作為基類。
//飛的多快 function speeds(birds) { return new Map(birds.map(b => [b.name, airSpeedVelocity(b)])); } //羽毛 function plumages(birds) { return new Map(birds.map(b => [b.name, plumage(b)])) } function plumage(bird) { return new Bird(bird).plumage; } function airSpeedVeliocity(bird) { return new Bird(bird).airSpeedVeliocity; } class Bird { constructor(birdObject) { Object.assign(this, birdObject); } get plumage() { switch (this.type) { case 'EuropeanSwallow': return "average"; case 'AfricanSwallow': return (this.numberOfCoconuts > 2) ? "tired" : "average"; case 'NorwegianBlueParrot': return (this.voltage > 100) ? 'scorched' : 'beautiful'; default: return 'unknow'; } } get airSpeedVelocity() { switch (this.type) { case 'EuropeanSwallow': return 35; case 'AfricanSwallow': return 40 - 2 * this.numberOfCoconuts; case 'NorwegianBlueParrot': return (this.isNailed) ? 0 : 10 + this.voltage / 10; default: return null; } } }
針對每種鳥創建一個子類,用一個工廠函數來實例化合適的子類對象。
class EuropeanSwallow { } class AfricanSwallow { } class NorwegianBlueParrot { } function createBird(bird) { switch (bird.type) { case 'EuropeanSwallow': return new EuropeanSwallow(bird); case 'AfricanSwallow': return new AfricanSwallow(bird); case 'NorwegianBlueParrot': return new NorwegianBlueParrot(bird); default: return new Bird(bird); } }
有了需要的類結構,現在處理兩個條件邏輯。
先從plumage函數開始,從switch語句中選一個分支,在適當的子類中覆寫這個邏輯。
class EuropeanSwallow { get plumage(){ return "average"; } } 超類Bird中EuropeanSwallow邏輯分支改為拋出異常。 get plumage() { switch (this.type) { case 'EuropeanSwallow': throw "oops"; case 'AfricanSwallow': return (this.numberOfCoconuts > 2) ? "tired" : "average"; case 'NorwegianBlueParrot': return (this.voltage > 100) ? 'scorched' : 'beautiful'; default: return 'unknow'; } }
接著處理下一個分支,最后超類Bird從
get plumage() { switch (this.type) { case 'EuropeanSwallow': throw 'oops'; case 'AfricanSwallow': throw 'oops'; case 'NorwegianBlueParrot': throw 'oops'; default: return 'unknow'; } }
變成
get plumage() { return 'unknow'; }
airSpeedVelocity也如法炮制,
然后對頂層的airSpeedVelocity和plumage做了內聯處理,
從
function plumages(birds) { return new Map(birds.map(b => [b.name, plumage(b)])) } function plumage(bird) { return new Bird(bird).plumage; }
變成
function plumages(birds) { return new Map(birds .map(b => createBird(b)) .map(bird => [b.name, bird.airSpeedVelocity)) }
最終完成后的代碼如下:
//飛的多快 function speeds(birds) { return new Map(birds .map(b => createBird(b)) .map(bird => [b.name, bird.plumage])); } //羽毛 function plumages(birds) { return new Map(birds .map(b => createBird(b)) .map(bird => [b.name, bird.airSpeedVelocity)) } function createBird(bird) { switch (bird.type) { case 'EuropeanSwallow': return new EuropeanSwallow(bird); case 'AfricanSwallow': return new AfricanSwallow(bird); case 'NorwegianBlueParrot': return new NorwegianBlueParrot(bird); default: return new Bird(bird); } } class Bird { constructor(birdObject) { Object.assign(this, birdObject); } get plumage() { return 'unknow'; } get airSpeedVelocity() { return null; } } class EuropeanSwallow extends Bird { get plumage() { return "average"; } get airSpeedVelocity() { return 35; } } class AfricanSwallow extends Bird { get plumage() { return (this.numberOfCoconuts > 2) ? "tired" : "average"; } get airSpeedVelocity() { return 40 - 2 * this.numberOfCoconuts; } } class NorwegianBlueParrot extends Bird { get plumage() { return (this.voltage > 100) ? 'scorched' : 'beautiful'; } get airSpeedVelocity() { return (this.isNailed) ? 0 : 10 + this.voltage / 10; } }
二、多態處理變體邏輯
2021-03-11
如果覺得本文對您有幫助~可以微信支持一下:




浙公網安備 33010602011771號