复杂的条件逻辑是编程中最难理解的东西之一,给条件逻辑添加结构。
可以将条件逻辑拆分到不同的场景(或者叫高阶用例),从而拆分条件逻辑。使用类和多态能把逻辑的拆分表述得更清楚,多态是改善复杂条件逻辑的有力工具。
有两种常见场景,
一种是,好几个函数都有基于类型的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