В нашем обычном развитии, если еще является наиболее часто используемым утверждением условного суждения. В некоторых простых сценариях if else очень удобно использовать, но в немного более сложной логике большое количество if else заставит других выглядеть ошеломленными.
Если кто-то еще хочет пересмотреть или добавить условие, продолжайте добавлять к нему условия. Если этот порочный круг продолжится, то лишь немногие из них могут в конечном итоге стать десятками или даже десятками.
Не говорите, что это невозможно, я видел, как люди часто используют if else в компонентах React, а читабельность и ремонтопригодность очень плохие. (Конечно, это не горшок если еще, это в основном проблема дизайна компонентов)
Эта статья в основном взята из "Энциклопедии кода 2". В оригинальной книге для реализации используются vb и java. Здесь я основываюсь на реализации TypeScript и добавляю к содержанию книги кое-что из своего собственного понимания.
Начните с примера
календарь
Если мы хотим сделать компонент календаря, то мы должны знать, сколько дней в каждом месяце из 12 месяцев в году, как мы об этом судим?
Самый глупый способ, конечно, использовать if else.
if (month === 1) {
return 31;
}
if (month === 2) {
return 28;
}
...
if (month === 12) {
return 31;
}
Таким образом, необходимо написать 12 ifs сразу, что тратит много времени и неэффективно.
В настоящее время некоторые люди будут думать об использовании switch/case для этого, но switch/case будет не намного проще, чем if, и все равно нужно будет написать 12 case! ! ! Что, если бы учитывались високосные годы? Не будет ли это более хлопотным?
Мы могли бы также изменить наше мышление.Каждый месяц соответствует числу, а месяцы идут по порядку.Можем ли мы использовать массив для хранения количества дней? Когда он будет доступен по индексу?
const month: number = new Date().getMonth(),
year: number = new Date().getFullYear(),
isLeapYear: boolean = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
const monthDays: number[] = [31, isLeapYear ? 29 : 28, 31, ... , 31];
const days: number = monthDays[month];
концепция
Я полагаю, что после прочтения приведенного выше примера у вас есть определенное представление о табличном методе. Вот краткое изложение «Энциклопедии кода».
Табличный подход — это модель программирования, которая ищет информацию в таблице без использования логических операторов. На самом деле все, что можно выбрать с помощью логических операторов, можно выбрать, просматривая таблицу. Для простых случаев проще и понятнее использовать логические операторы. Но по мере того, как логическая цепочка становится все более и более сложной, метод справочной таблицы становится все более и более привлекательным.
Прежде чем использовать табличный метод, вам нужно подумать над двумя вопросами. Один из них — как делать запросы из таблицы. В конце концов, не все сценарии так просты, как описанные выше. Если оценка if находится в другом диапазоне, как Проверь это?
Другой вопрос, что вам нужно запрашивать в таблице, это данные? или действие? Или индекс?
На основе этих двух вопросов запросы делятся на следующие три типа:
- прямое интервью
- доступ к индексу
- Лестничный доступ
таблица прямого доступа
Календарь, который мы представили выше, является хорошим примером прямого доступа к таблице, но во многих случаях это не так просто.
Статистика страховых тарифов
Предположим, вы пишете программу для страховых ставок.Эта ставка будет меняться в зависимости от различных условий, таких как возраст, пол, семейное положение и т. д. Если вы используете логические управляющие структуры (if, switch) для представления разных ставок, это будет очень проблематично. .
if (gender === 'female') {
if (hasMarried) {
if (age < 18) {
//
} else {
//
}
} else if (age < 18) {
//
} else {
//
}
} else {
...
}
Но из приведенного выше примера с календарем этот возраст представляет собой диапазон, а не фиксированное значение, и его нельзя сопоставить с массивом или объектом, так что же нам делать? Это связано с проблемой, упомянутой выше, как сделать запрос из таблицы?
Эта проблема может быть решена двумя методами: таблицей доступа к релейной диаграмме и таблицей прямого доступа, доступ к релейной диаграмме будет представлен позже, и здесь упоминается только таблица прямого доступа.
Есть два обходных пути:
1. Скопируйте информацию, чтобы иметь возможность напрямую использовать значение ключа.
Мы можем скопировать часть информации для каждого возраста в диапазоне от 1 до 17 лет, а затем использовать возраст для прямого доступа к ней, и то же самое верно для других возрастных групп. Этот метод заключается в том, что операция очень проста, и структура таблицы также очень проста. Но есть недостаток в том, что это будет занимать место впустую, ведь генерируется много избыточной информации.
2. Преобразование пары "ключ-значение"
Давайте подумаем об этом с другой стороны, что, если мы преобразуем возрастной диапазон в ключ? Таким образом, к нему можно получить прямой доступ.Единственная проблема, которую следует рассмотреть, — это как преобразовать возраст в значение ключа.
Конечно, мы можем продолжить это преобразование с помощью if else. Как упоминалось ранее, с простым if else проблем не возникает, а управление таблицами предназначено только для оптимизации сложных логических суждений, что делает их более гибкими и легко расширяемыми.
enum ages {
unAdult = 0
adult = 1
}
enum genders {
female = 0,
male = 1
}
enum marry = {
unmarried = 0,
married = 1
}
const age2key = (age: number): string => {
if (age < 18) {
return ages.unAdult
}
return ages.adult
}
type premiumRateType = {
[ages: string]: {
[genders: string]: {
[marry: string]: {
rate: number
}
}
}
}
const premiumRate: premiumRateType = {
[ages.unAdult]: {
[genders.female]: {
[marry.unmarried]: {
rate: 0.1
},
[marry.married]: {
rate: 0.2
}
},
[genders.male]: {
[marry.unmarried]: {
rate: 0.3
},
[marry.married]: {
rate: 0.4
}
}
},
[ages.adult]: {
[genders.female]: {
[marry.unmarried]: {
rate: 0.5
},
[marry.married]: {
rate: 0.6
}
},
[genders.male]: {
[marry.unmarried]: {
rate: 0.7
},
[marry.married]: {
rate: 0.8
}
}
}
}
const getRate = (age: number, hasMarried: 0 | 1, gender: 0 | 1) => {
const ageKey: string = age2key(age);
return premiumRate[ageKey]
&& premiumRate[ageKey][gender]
&& premiumRate[ageKey][gender][hasMarried]
}
Таким образом, после изменения условия оценки вам нужно будет только изменить данные в premiumRate.
Но я думаю, что этот пример все еще недостаточно хорош, и тогда я подумал о некоторых способах его оптимизации, чтобы было легче понять, изменив код следующим образом.
enum ages {
unAdult = 0,
adult = 1
}
enum genders {
female = 0,
male = 1
}
enum marry = {
unmarried = 0,
married = 1
}
const age2key = (age: number): string => {
if (age < 18) {
return ages.unAdult
}
return ages.adult
}
const rates: number[] = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]
const premiumRate = {
0: [age.unAdult, genders.female, marry.unmarried],
1: [age.unAdult, genders.female, marry.married],
2: [age.unAdult, genders.male, marry.unmarried],
3: [age.unAdult, genders.male, marry.unmarried],
4: [age.adult, genders.female, marry.unmarried],
5: [age.adult, genders.female, marry.married],
6: [age.adult, genders.male, marry.unmarried],
7: [age.adult, genders.male, marry.unmarried]
}
type BoolCode = 0 | 1
const getRate = (age: number, hasMarried: BoolCode, gender: BoolCode) => {
const ageKey: BoolCode = age2key(age)
let index: string = ''
Object.keys(premiumRate).forEach((key, i) => {
const condition: BoolCode[] = premiumRate[key]
if (condition[0] === ageKey
&& condition[1] === gender
&& condition[2] === hasMarried
) {
index = key;
}
})
return rates[index];
}
После этой модификации структура становится более четкой и легкой в обслуживании.
Индекс таблицы доступа
Стоящий перед нами вопрос страхового тарифа — это головная боль при работе с возрастным диапазоном, который часто не так легко получить, как ключ выше.
Мы упомянули копирование информации в то время, чтобы иметь возможность напрямую использовать значение ключа, но этот метод тратит много места, потому что каждый возраст будет сохранять часть данных, но что, если мы просто сохраним индекс и запросим данные через этот индекс?
Предполагая, что человек родился в 0 лет и может прожить до 100 лет, тогда нам нужно создать массив длиной 101, а нижний индекс массива соответствует возрасту человека, чтобы мы хранили '65' выше 65.
Таким образом, мы можем получить соответствующий индекс по возрасту, а затем запросить соответствующие данные по индексу.
Кажется, что этот метод сложнее, чем приведенная выше таблица прямого доступа, но в некоторых сценариях, когда сложно преобразовать ключевые значения, а данные занимают много места, можно попробовать доступ по индексу.
const ages: string[] = ['<18', '<18', '<18', '<18', ... , '18-65', '18-65', '18-65', '18-65', ... , '>65', '>65', '>65', '>65']
const ageKey: string = ages[age];
Форма доступа к лестнице
То же самое для решения проблемы вышеуказанного возрастного диапазона: доступ к лестнице не такой прямой, как доступ к индексу, но он сэкономит больше места.
Чтобы использовать метод лестницы, вам нужно записать верхний предел каждого интервала в таблицу, а затем зациклить, чтобы проверить интервал, в котором находится возраст, поэтому обязательно проверяйте конечные точки интервала при использовании доступа к лестнице. .
const ageRanges: number[] = [17, 65, 100],
keys: string[] = ['<18', '18-65', '>65'],
len: number = keys.length;
const getKey = (age: number): string => {
for (let i = 0; i < len; i++) {
console.log('i', i)
console.log('ageRanges', ageRanges[i])
if (age <= ageRanges[i]) {
return keys[i]
}
}
return keys[len-1];
}
Лестничный доступ подходит для сценариев, в которых доступ к индексу неприменим, например, если это число с плавающей запятой, вы не можете использовать доступ к индексу для создания массива для получения индекса.
В случае относительно большого объема данных рассмотрите возможность использования бинарного поиска вместо последовательного поиска.
В большинстве случаев предпочтительнее прямой доступ и индексированный доступ, а также рассматривается многоуровневый доступ, если только оба варианта действительно невозможно обработать.
С точки зрения этих трех таблиц доступа в основном нужно решить, как выполнять запросы из таблицы, и в различных сценариях следует использовать соответствующие таблицы доступа.
Смысл табличного управления состоит в том, чтобы разделить данные и логику.В разработке безопаснее изменять конфигурацию напрямую, чем изменять логику. Добавление и удаление данных менее рискованно, чем добавление и удаление логических условий, а источник данных более гибкий.
Цитируя отрывок из Zhihu Da V Ivony:
При анализе и чтении фрагмента кода он часто фокусируется на аспектах, иногда на данных, иногда на логике. Предположим, у нас есть такое требование, когда значение такого-то меньше 100, как оно. Тогда 100 в этом — это данные.Что происходит, когда спрос изменяется до определенного значения меньше 200, то наше внимание сосредоточено на модификации этих данных. Вместо модификации всей логики удаление данных помогает нам найти точки модификации и быстрее модифицировать код.
Использованная литература:
PS: Приглашаю всех обратить внимание на мой публичный аккаунт [Front-end Pavilion], и давайте вместе обсудим технологии.