前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >如何优化冗长的条件语句

如何优化冗长的条件语句

作者头像
進无尽
发布于 2018-09-12 08:50:53
发布于 2018-09-12 08:50:53
1.3K00
代码可运行
举报
文章被收录于专栏:進无尽的文章進无尽的文章
运行总次数:0
代码可运行

前言

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
我不讨厌简短的 if else,但是对于很长并且负责的 if else 就极其感到不舒服了,代码不但看起来难懂不雅,
关键是维护起来也是一大坨,生怕弄错了之前的逻辑。

OO设计遵循SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)原则,
使用这个原则去审视if/else,可能会发现很多问题,比如不符合单一原则,
它本身就像一团浆糊,融合了各种作料,黏糊糊的很不干净;
比如不符合开闭原则,每新增一种场景,就需要修改源文件增加一条分支语句,
业务逻辑复杂些若有1000种场景就得有1000个分支流,这种情况下代码不仅仅恶心问题了,效率上也存在很大问题。
由此可见,if/else虽然简单方便,但不恰当的使用会给编码代码带来非常痛苦的体验。
针对这种恶心的if/else分支,我们当然首先想到的去重构它--在不改变代码外部功能特征的前提下对代码内部逻辑进行调整和优化,

而且《重构》一书上有讲到这个问题。if...else, swith...case 是面向过程的代码,在面向对象的代码中应尽可能少地出现。

四个优化方向

【1】尽量少用 else 尽量多用 if reture 的语法方式。 【2】字典的逻辑对应转化作用。 【3】用多态替代条件语句 【4】策略模式,继承重写,抽象父类和统一的接口入口。

一、尽量少用 else 尽量多用 if reture 的语法方式

当一些条件语句难以让人看清他的目的时,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)showName:(NSString *)name
{
    if (name != nil)
    {
        if (name.length > 0)
        {
            NSLog(@"showName");
        }
        else
        {
            NSLog(@"name.length is zero");
        }
    }
    else
    {
        NSLog(@"name is nil");
    }
}

我们可以用卫语句来使得主体逻辑更加清晰尽量不使用 else

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)showName:(NSString *)name
{
    if (name == nil){
        NSLog(@"name is nil");
        return;
    }
    if (name.length == 0){
        NSLog(@"name.length is zero");
        return;
    }
    NSLog(@"showName");
}

二、字典的逻辑对应转化作用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (NSDictionary *)strategyDict{
    if (_strategyDict == nil) {
        _strategyDict = @{
                          @"day1" : [self invocationWithMethod:@selector(playBasketball:)],
                          @"day2" : [self invocationWithMethod:@selector(shopping:)],
                          @"day3" : [self invocationWithMethod:@selector(washClothes:)],
                          @"day4" : [self invocationWithMethod:@selector(playGames:)],
                          @"day5" : [self invocationWithMethod:@selector(sing:)]
             };
    }
    return _strategyDict;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
你会发现字典(哈希表)map是很神奇一种数据结构,再多考虑一步:
1.map的value中保存的不再是基本数据类型,而是对象。
  这样一来,通过不同的key可以拿到不同的对象,如果这些对象的类都实现同一个接口,那么这就是一个加强版的策略模式,
  就是多态性的体现,传统的策略模式传入的是实现类的对象,而通过map加强,只需传入一个数字或字符串即可实现多态。

2.map的value中保存的是函数,通过不同的key(消息类型)可以拿到不同的响应处理函数,则可以实现消息机制或事件驱动。

三、 用多态替代条件语句

使用多态的场景

  • 当对象要根据不同的状态表现不同的行为时。
  • 当你需要在很多地方检查相同的条件时。

我们来看简单的一个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Class Update {
    execute() {
        if (FLAG_i18n_ENABLE) {
            //DO A;
        } else {
            //DO B;
        }
    }
    render() {
        if (FLAG_i18n_ENABLE) {
            //render A;
        } else {
            //render B;
        }
    }
}

那么,如何用多态来重写上面的类呢? 我们可以分为两步来操作: - 让 Update 成为抽象类,方法也抽象。 - 在子类中的覆盖方法实现条件语句的分支操作。

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract class Update {
    abstract execute();
    abstract render();
}
class I18NUpdate extends Update {
    execute() {
        //Do A;
    }
    render() {
        //render A;
    }
}
class NonI18NUpdate extends Update {
    execute() {
        //Do B;
    }
    render() {
        //render B;
    }
}

测试方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void testExecuteDoA() {
    Update u = new I18NUpdate();
    u.execute();
    assertX();
}
void testExecuteDoB() {
    Update u = new NonI18NUpdate();
    u.execute();
    assertX();
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
用多态实现的类,通过继承抽象类,重写抽象方法的方式,避免使用了条件语句。
在测试的时候,不需要关心它的状态码,子类本身就已经承载了状态信息。
所以你可以看到,在测试的时候,代码非常的清晰易懂。

使用多态实现的类有两个好处:

  • 我们可以通过增加新的子类来添加新的行为,而且不会影响到原来的代码。
  • 不同的操作和概念在不同的类中,容易理解和阅读。

这个例子太简单,可以看这篇文章中的例子:使用state pattern替代if else,就会发现使用多态替代条件语句不但优雅化了,而且在复杂的情况下是必须要这样处理了。这是一种全新的解决需求扩展和提高项目可维护性的方法。

四、策略模式优化条件语句

策略模式的定义

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
也叫政策模式,定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
策略模式使用的就是面向对象的继承和多态机制,由三个角色构成

1、Rescue封装角色
  也叫上下文角色,起承上启下的封装作用,屏蔽高层模块对策略、算法的直接访问、封装可能存在的变化。
2、Strategy抽象策略角色
  策略、算法家族的抽象,通过为接口,定义每个策略或算法必须具有的方法和属性。
3、ConcreteStrategy具体策略角色
    实现抽象策略中的操作,该类含有具体的算法。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
首先建立一个通用的策略,这里直接命名为Strategy。这个类是最终暴露出来,需要被调用的类。
//Strategy.h
#import <Foundation/Foundation.h>
@interface Strategy : NSObject
- (void) go;
@end

这个类比较简单,只定义了一个go方法。
//Strategy.m
#import "Strategy.h"
@implementation Strategy
- (void)go{
    NSLog(@"I am going outside");
}
@end

接下来定义两个类,分别继承自Strategy类,这两个类中包含了具体的方法实现,是功能的主体部分。

//OldPeopleTravel.h
#import <Foundation/Foundation.h>
#import "Strategy.h"
@interface OldPeopleTravel : Strategy
- (void) go;
@end
.m文件里是具体的针对对老年人的实现方法

//OldPeopleTravel.h
#import "OldPeopleTravel.h"
@implementation OldPeopleTravel

-(void)go{
    [super go];
    NSLog(@"I am old, I need rest");
}

@end

类似的还有YoungPeopleTravel的.h和.m文件

//YoungPeopleTravel.h
#import "Strategy.h"

@interface YoungPeopleTravel : Strategy
- (void) go;
@end

//YoungPeopleTravel.m
#import "YoungPeopleTravel.h"
@implementation YoungPeopleTravel
- (void) go{
    [super go];
    NSLog(@"I am young, I am energetic");
}
@end

以上是策略类和具体的实现类的实现,接下来就是调用这个策略了。

//ViewController.m
#import "ViewController.h"
#import "Strategy.h"
#import "OldPeopleTravel.h"
#import "YoungPeopleTravel.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self goOutside:[[OldPeopleTravel alloc]init]];
    [self goOutside:[[YoungPeopleTravel alloc]init]];
}

- (void)goOutside:(id)theStrategy{
    Strategy *strategy = theStrategy;
    [strategy go];
}

@end
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
可以看到,最终我们调用的是自己的goOutside方法,方法中有一个参数是strategy,
通过传入不同的参数(策略),就可以调用这个策略下具体的方法实现。运行结果表示策略模式已经成功的实现了。
通过调用不同的策略,得到了不同的处理结果。

多态和策略模式之间的联系

我们看完上面的第三中方法(用多态替代条件语句) 和 第四种方法(策略模式优化条件语句)没有感觉两者很相似,其实两者的侧重点不同。

多态性的定义是:同一操作作用于不同的类的实例,将产生不同的执行结果,即不同类的对象收到相同的消息时,得到不同的结果。多态是面向对象程序设计的重要特征之一,是扩展性在“继承”之后的又一重大表现 。对象根据所接受的消息而做出动作,同样的消息被不同的对象接受时可能导致完全不同的行为,这种现象称为多态性。

【1】首先多态是高层次,高度抽象的概念,独立于语言之外,是面向对象思想的精髓,而策略模式只是一种软件设计模式,相对而言更加具体 【2】其次,多态更多强调的是,不同的对象调用同一个方法会得到不同的结果,而策略模式更多强调的是,同一个对象(事实上这个对象本身并不重要)在不同情况下执行不同的方法,而他们的实现方式又是高度类似的,即共享同一个抽象父类并且各自重写父类的方法。 【3】策略模式是通过多态来实现不同子类的选取,是多态调用具体算法的展现。

总结

条件语句的优化,不是上述一种方式可以完成的,往往是上述几种方法的结合使用。

参考文章:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.07.26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
js数组中一些实用的方法(forEach,map,filter,find)
需求场景: 假若后端返回这么一个json数据格式,如下所示,我们需要拿到返回对象中的数组项,或者根据某些指定的条件,取特定的值,然后渲染到页面当中去,例如:拿name属性值
itclanCoder
2020/10/28
2.9K0
js数组中一些实用的方法(forEach,map,filter,find)
JavaScript —— Array 使用汇总
由于 length 和 prototype 两个属性比较通用,所以这里不过多的介绍。
Originalee
2020/06/11
6180
JavaScript —— Array 使用汇总
js数组笔记
数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始)。整个数组用方括号表示,数组的值用','分割;数组的数据可以是任何类型。
bamboo
2019/01/29
11.8K0
js数组笔记
爆 肝 一 周 总 结 最全 JavaScript Array 方法详解
我们在日常开发中,与接口打交道最多了,前端通过访问后端接口,然后将接口数据二次处理渲染到页面当中。
程序员海军
2022/02/15
8010
爆 肝 一 周 总 结 最全 JavaScript Array 方法详解
最全 JavaScript Array 方法 详解
我们在日常开发中,与接口打交道最多了,前端通过访问后端接口,然后将接口数据二次处理渲染到页面当中。 二次处理的过程是 考验 Coder 对 Array 是否熟练 以及 在 何种 场景下使用哪种方法处理最优 。 小编,在最近开发中就遇到了 Array 问题, 在处理复杂的业务需求时,没想到Array 有类似的方法,然后将方法 组合起来解决当下问题。
程序员海军
2021/10/11
1.1K0
最全 JavaScript Array 方法 详解
js 数组详细操作方法及解析
目的:Array.of() 出现的目的是为了解决上述构造器因参数个数不同,导致的行为有差异的问题。
kif
2023/02/27
1.3K0
forEach和map的区别?
forEach和map是JavaScript中常用的数组迭代方法,它们有以下几个主要区别:
王小婷
2023/10/27
7440
Javascript数组操作
使用JS也算有段时日,然对于数组的使用,总局限于很初级水平,且每每使用总要查下API,或者写个小Demo测试下才算放心,一来二去,浪费不少时间;思虑下,堪能如此继续之?当狠心深学下方是正道。 一, 数组常用方法 1. 数组的创建 var arrayObj = new Array(); //创建一个数组 var arrayObj = new Array([size]); //创建一个数组并指定长度,注意不是上限,是长度 var arrayObj = new Array([element0[, element1
晚晴幽草轩轩主
2018/03/27
3.9K0
js中的四种for循环
最近刷题时遇到了几种不同for循环,因为没有深入了解导致做题时无法区分它们的用法,尤其是在以及在使用时的注意点。
用户7741497
2022/03/06
1.9K0
前端面试题---JS部分
转载链接:https://blog.csdn.net/qq_54753561/article/details/122149197
用户8087287
2022/10/31
7660
前端面试题---JS部分
原生 JavaScript 手写数组 API
本文将会先了解数组 API 的用法再模拟实现这些 API ,如果各位大佬觉得有什么不对的地方麻烦指点以下!
小丞同学
2021/08/16
7660
Js中Array对象
JavaScript的Array对象是用于构造数组的全局对象,数组是类似于列表的高阶对象。
WindRunnerMax
2020/10/26
9.9K0
通过事例重温一下常见的 JS 中 15 种数组操作(备忘清单)
数组是 JS 中广泛使用的数据结构。数组对象提供了大量有用的方法,如array. forEach()、array.map()等来操作数组。
前端达人
2019/12/24
1.2K0
[第27 期]彻底分清Javascript forEach & map
JavaScript 中,数组的遍历我们肯定都不陌生,最常见的两个便是forEach 和 map。
皮小蛋
2020/03/02
4830
Js数组操作
JavaScript数组操作,主要包括Array对象原型方法以及常用操作如去重、扁平化、排序等。
WindRunnerMax
2020/08/27
16.9K0
JavaScript数组
数组实例的find()方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
六个周
2022/10/28
7210
for 循环的 5 种写法,哪种最快?
来源:juejin.im/post/5ea63f3ef265da47b177b4b6
Leetcode名企之路
2021/10/08
9780
前端学习(40)~js学习(十七):数组的常见方法&数组的遍历
unshift():在数组最前面插入一个或多个元素,返回结果为该数组新的长度。插入元素后,其他元素的索引会依次调整。
Vincent-yuan
2020/03/19
1.9K0
JavaScript中的forEach,你踩过哪些坑?请避开这些常见误区
在JavaScript的世界里,forEach是我们常用的数组遍历方法之一。大多数开发者都熟悉它的基础用法,但你知道吗?在处理异步操作时,forEach可能会让你掉进一些意想不到的“坑”。这篇文章将带你深入了解forEach的特性和局限,揭示一些你可能不知道的使用技巧和解决方案。无论你是前端新手,还是经验丰富的开发者,都能从中学到有用的知识,帮助你在实际项目中避开那些隐藏的陷阱。准备好了吗?让我们一探究竟!
前端达人
2024/11/25
2220
JavaScript中的forEach,你踩过哪些坑?请避开这些常见误区
JS数组常用方法大全
join(separator): 将数组的元素组起一个字符串,以separator为分隔符,省略的话则用默认用逗号为分隔符,该方法只接收一个参数:即分隔符。
青梅煮码
2023/01/14
3K0
相关推荐
js数组中一些实用的方法(forEach,map,filter,find)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文