↓↓↓

There’s Such a Thing As Using Too Many Ifs有一种事情是使用太多的如果

↑↑↑

May 7th, 2019 – by

Alexander Dreith

May 7th, 2019 –

亚历山大·德雷思

↓↓↓

How Many Is Too Many?多少是太多了?

↑↑↑

Some people think that number is one and you should always substitute at least a ternary for any single if statements. I don’t take that staunch of an approach, but I want to highlight some ways to escape common if/else spaghetti code.有些人认为这个数字是 1,你应该总是用至少一个三元来代替任何单个 if 语句。我不采取这种坚定的方法,但我想强调一些逃避常见 if/else 意大利面条代码的方法。

I believe a lot of developers fall into the if/else trap so easily, not because of the complexity of other solutions, but because it follows such a natural language pattern:我相信很多开发人员很容易陷入 if/else 陷阱,不是因为其他解决方案的复杂性,而是因为它遵循这样的自然语言模式:
if something do this, else do this instead.if某些东西这样做,else为这样做。

↓↓↓

Wait, What’s a Ternary?等等,什么是三元?

↑↑↑

↓↓↓

I already know, let’s skip this.我已经知道了,让我们跳过这个。

↑↑↑

A ternary isn’t a revolutionary difference from an if/else as they’re both conditional operations, but a ternary does return a value so it can be used directly in an assignment.三元与 if/else 没有革命性的区别,因为它们都是条件运算,但三元确实返回一个值,因此可以直接在赋值中使用。

const greaterThanFive = (8 > 5) ? 'yep' : 'nope';
 
console.log(greaterThanFive); // 'yep'

The basic pattern is just a condition, one value to return if truthy, and one value to return if falsy.基本模式只是一个条件,如果为真,则返回一个值,如果为假,则返回一个值。

(condition) ? isTruthy : isFalsy

↓↓↓

An alternative to If/ElseIf/Else 的替代方法

↑↑↑

Let’s start with a scenario and walk through examples of different solutions.让我们从一个场景开始,并逐步介绍不同解决方案的示例。

We’ll be taking colors from user input and need to turn them into some preset color codes to match so we can change our background color. So we’ll check for strings of color names and set our color code if we have a match.我们将从用户输入中获取颜色,并需要将它们转换为一些预设的颜色代码进行匹配,以便我们可以更改背景颜色。因此,我们将检查颜色名称的字符串,并在匹配项中设置颜色代码。

const setBackgroundColor = (colorName) => {
	let colorCode = '';
	if(colorName === 'blue') {
		colorCode = '#2196F3';
	} else if(colorName === 'green') {
		colorCode = '#4CAF50';
	} else if(colorName === 'orange') {
		colorCode = '#FF9800';
	} else if(colorName === 'pink') {
		colorCode = '#E91E63';
	} else {
		colorCode = '#F44336';
	};
	document.body.style.backgroundColor = colorCode;
};

This if/else gets the job done. But we’re saddled with a lot of repetitive logic comparing colorName and repetitive assignment of colorCode.if/else 可以完成工作。但是我们背负着很多重复的逻辑,比较 colorNamecolorCode 的重复赋值。

↓↓↓

Switch开关

↑↑↑

Now we could more appropriately change this into a switch statement. It better fits the concept of what we’re trying to do; We have several cases of strings we want to match, and a default if none of our cases match.现在我们可以更恰当地将其更改为 switch 语句。它更符合我们正在努力做的事情的概念;我们有几个想要匹配的字符串大小写,如果我们的大小写都不匹配,则使用默认值。

const setBackgroundColor = (colorName) => {
	let colorCode = '';
	switch(colorName) {
		case 'blue':
			colorCode = '#2196F3';
			break;
		case 'green':
			colorCode = '#4CAF50';
			break;
		case 'orange':
			colorCode = '#FF9800';
			break;
		case 'pink':
			colorCode = '#E91E63';
			break;
		default:
			colorCode = '#f44336';
	};
	document.body.style.backgroundColor = colorCode;
};

But a switch still comes with a lot of boilerplate and repetitive code we could do without.但是 switch 仍然带有许多我们可以不用的样板和重复代码。

↓↓↓

Lookup Table查找表

↑↑↑

So what are we really trying to accomplish here? We need to assign color codes in hex to color names, so let’s create an object that holds color names as keys, and color codes as values. Then we can look up our color code by its color name by using object[key]. And we need a default value, so a short ternary that returns the default if no key is found will do so all while making the default part of our object.那么,我们真正想要实现的目标是什么呢?我们需要将十六进制的颜色代码分配给颜色名称,因此让我们创建一个对象,将颜色名称保存为键,将颜色代码保存为值。然后,我们可以使用 object[key] 按颜色名称查找颜色代码。我们需要一个默认值,因此,如果没有找到键,则返回默认值的短三元将这样做,同时将默认部分作为我们对象的默认部分。

const colorCodes = {
	'blue'   : '#2196F3',
	'green'  : '#4CAF50',
	'orange' : '#FF9800',
	'pink'   : '#E91E63',
	'default': '#F44336'
};
 
const setBackgroundColor = (colorName) => {
	document.body.style.backgroundColor = colorCodes[colorName]
		? colorCodes[colorName]
		: colorCodes['default'];
}; 
 
 
 
 

Now we have a lookup table that neatly lays out our inputs and possible outputs.现在我们有一个查找表,它整齐地列出了我们的输入和可能的输出。

This isn’t about a miraculous ‘lines of code’ (LOC) reduction (we went from 15 to 20 to 12.) In fact, some of these solutions may increase your LOC, but we’ve increased maintainability, legibility, and actually reduced complexity by only having one logic check for a default fallback.这并不是奇迹般的“代码行数”(LOC)减少(我们从15行到20行再到12行)。事实上,其中一些解决方案可能会_增加_您的 LOC,但我们通过仅对默认回退进行一次逻辑检查,提高了可维护性和易读性,并实际上降低了复杂性。

↓↓↓

Trade Logic For Data数据交易逻辑

↑↑↑

The most important accomplishment of using a lookup table over an if/else or switch is that we’ve turned multiple instances of comparative logic into data. The code is more expressive; it shows the logic as an operation. The code is more testable; the logic has been reduced. And our comparisons are more maintainable; they’re consolidated as pure data.在 if/elseswitch上使用查找表最重要的成就是,我们已经将比较逻辑的多个实例转换为数据。代码更具表现力;它将逻辑显示为操作。代码更具可测试性;逻辑已减少。而且我们的比较更易于维护;它们被合并为纯数据。

Let’s reduce five comparative logic operations to one and transform our values into data.让我们将五个比较逻辑运算减少到一个,并将我们的值转换为数据。

Scenario: we need to convert grade percentages into their letter grade equivalent.场景:我们需要将成绩百分比转换为字母成绩等效物。

An if/else is simple enough; we check from top down if the grade is higher or equal than what’s needed to match the letter grade.if/else 很简单;我们自上而下检查等级是否高于或等于匹配字母等级所需的等级。

const getLetterGrade = (gradeAsPercent) => {
	if(gradeAsPercent >= 90) {
		return "A";
	} else if(gradeAsPercent >= 80) {
		return "B";
	} else if(gradeAsPercent >= 70) {
		return "C";
	} else if(gradeAsPercent >= 60) {
		return "D";
	} else {
		return "F"
	};
};

But we’re repeating the same logic operation over and over.但是我们一遍又一遍地重复相同的逻辑操作。

So let’s extract our data into an array (to retain order) and represent each grade possibility as an object. Now we only have to do one >= comparison on our objects and find the first in our array that matches.因此,让我们将数据提取到一个数组中(以保持顺序),并将每个等级可能性表示为一个对象。现在我们只需要对我们的对象进行一次 >= 比较,并在数组中找到第一个匹配的对象。

const gradeChart = [
	{minpercent: 90, letter: 'A'},
	{minpercent: 80, letter: 'B'},
	{minpercent: 70, letter: 'C'},
	{minpercent: 60, letter: 'D'},
	{minpercent: 0,  letter: 'F'}
];
 
const getLetterGrade = (gradeAsPercent) => {
	const grade = gradeChart.find(
		(grade) => gradeAsPercent >= grade.minpercent
	);
 
	return grade.letter;
};

↓↓↓

Start Imagining Your Comparisons As Data开始将您的比较想象为数据

↑↑↑

When you need to compare or “check” values it’s natural to reach for the if/else so you can verbally step through the problem. But next time try to think of how your values can be represented as data and your logic reduced to interpret that data.当您需要比较或“检查”值时,很自然地会伸手去拿 if/else,这样您就可以口头解决问题。但下次试着想想如何将你的值表示为数据,并简化你的逻辑来解释这些数据。

Your code will end up being more readable, maintainable, and purposeful in its intent with a clear separation of the concepts it represents. All of the code examples here function, but the right approach can turn It Works™ Code into ‘great to work with’ code.您的代码最终将更具可读性、可维护性和目的性,并明确分离它所代表的概念。这里的所有代码示例都可以使用,但正确的方法可以将 It Works™ Code 变成“很好用”的代码。