zoukankan      html  css  js  c++  java
  • 重构-以多态取代条件表达式

    复杂的条件逻辑是编程中最难理解的东西之一,给条件逻辑添加结构

    可以将条件逻辑拆分到不同的场景(或者叫高阶用例),从而拆分条件逻辑。使用类和多态能把逻辑的拆分表述得更清楚,多态是改善复杂条件逻辑的有力工具。

    有两种常见场景,

    一种是,好几个函数都有基于类型的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;
        }
    }
    View Code

    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;
            }
        }
    }
    View Code

    针对每种鸟创建一个子类,用一个工厂函数来实例化合适的子类对象。

    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;
        }
    }
    View Code

    二、多态处理变体逻辑

    2021-03-11

    如果觉得本文对您有帮助~可以支付宝(左)或微信支持一下:


    看到小伙伴打赏时给我写一些鼓励的话,真的非常感动,谢谢你们。


    我开了个微信公众号(第三个二维码)用来分享自己的职场英语相关学习经验,感兴趣可以关注,我会不断更新~


    微信打赏微信公众号

  • 相关阅读:
    在Spring Boot中使用Swagger2
    初识设计模式之简单工厂模式、工厂方法模式、抽象工厂模式
    基于百度AI接口的微信小程序-数字识别
    微信小程序实现自定义遮罩
    微信小程序之视图容器movable-view实现拖拽功能
    微信小程序之map组件初体验
    基于百度AI接口的微信小程序-人脸搜索
    基于百度AI接口的微信小程序-图像搜索
    微信小程序实现上拉加载(分页加载)
    微信小程序实现下拉刷新
  • 原文地址:https://www.cnblogs.com/starof/p/14515461.html
Copyright © 2011-2022 走看看