Снимите колеса обучения программированию!

Мой партнер недавно записался в виртуальный учебный лагерь по программированию. Наблюдая за тем, как она выполняет свои первые уроки программирования, я был удивлен, обнаружив, что многое из того, что я узнал в школе, не работает.

В этой статье мы определим четыре программные конструкции, которые, возможно, были основополагающими, когда вы впервые узнаете, как работает программирование, но замедлят вас на следующем этапе вашего пути.

Отучиться от петель

Циклы - это одна из первых конструкций, которую вы изучаете, когда изучаете компьютерное программирование. Простой цикл while демонстрирует мощь автоматизации, когда вы можете многократно выполнять набор инструкций, пока не будет выполнено условие.

У вас может возникнуть соблазн использовать цикл while или for всякий раз, когда вы видите коллекцию элементов, но это редко бывает лучшим методом.

const groceries = [
  {
    name: 'Face Masks',
    price: 17.50,
  },
  {
    name: 'Disinfecting Wipes',
    price: 24.99,
  },
  {
    name: 'Goggles',
    price: 8.99,
  },
  {
    name: 'Gloves',
    price: 25.99,
  },
  {
    name: 'Hand Sanitizers',
    price: 24.99,
  },
];

Например, для массива объектов, где каждый объект представляет собой продуктовый магазин. Что бы вы сделали, если бы захотели распечатать название каждого продукта?

let index = 0;
while (index < groceries.length) {
  console.log(groceries[index].name);
  index = index + 1;
}

Этот цикл while, безусловно, достигает того, чего вы хотите, но вам нужно отслеживать индекс, чтобы обращаться к массиву продуктов каждый раз, когда вы хотите захватить элемент. Также существует риск зайти в бесконечный цикл, если вы забудете увеличить индекс.

Рассмотрим этот альтернативный подход:

groceries.forEach((item) => {
  console.log(item.name);
});

ForEach - это функция высшего порядка, которая принимает другую функцию в качестве аргумента и выполняет предоставленную функцию один раз для каждого элемента в массиве. Используя forEach, мы удаляем посторонний код для отслеживания и доступа к массиву с помощью индекса и увеличиваем масштаб нашей логики - распечатываем название каждого продукта.

Циклы while и for обычно слишком широки, чтобы уловить замысел нашего кода. Если вы хотите написать лучший код, начните с замены циклов функциями с большей специфичностью.

Ниже приведены еще несколько связанных примеров изменения списка и его обобщения.

// Before:
let index = 0;
const prices = [];
while (index < groceries.length) {
  prices.push(groceries[index].price);
  index = index + 1;
}
// After:
groceries.map((item) => {
  return item.price;
});
// Before:
let index = 0;
let total = 0;
while (index < groceries.length) {
  total = total + groceries[index].price;
  index = index + 1;
}
// After:
groceries.reduce((sum, item) => {
  return sum += item.price;
}, 0);

Отучиться от условных обозначений

Каждый раз, когда вы добавляете инструкцию else, вы увеличиваете сложность кода в два раза. Условные конструкции, такие как if-else и операторы switch, являются основополагающими блоками в мире программирования. Но они также могут мешать, когда вы хотите писать чистый, расширяемый код.

К счастью, есть несколько уловок, позволяющих избавиться от условных выражений.

Структуры данных

Рассмотрим следующую функцию для расчета скидки:

const discount = (amount, code) => {
  switch (code) {
    case 'DIJFNC':
      return amount * 0.80;
    case 'XPFJVM':
      return amount * 0.75;
    case 'FJDPCX':
      return amount * 0.50;
  }
};

Вы должны добавлять новый случай в оператор switch каждый раз, когда хотите добавить новый код скидки. А если ошибешься - нарушишь весь расчет.

Теперь давайте заменим условное выражение на объект.

const DISCOUNT_MULTIPLIER = {
  'DIJFNC': 0.80,
  'XPFJVM': 0.75,
  'FJDPCX': 0.50,
};
const discount = (amount, code) => {
  return amount * DISCOUNT_MULTIPLIER[code];
};

Эта перезапись эффективно отделяет данные от основной вычислительной логики, что упрощает их независимое изменение.

Полиморфизм

Второй способ заменить условные выражения - использовать ключевую особенность объектно-ориентированных языков программирования - полиморфизм.

const checkout = (amount, paymentMethod) => {
  switch (paymentMethod) {
    case 'credit-card':
      // Complex code to charge ${amount} to the credit card.
      break;
    case 'debit-card':
      // Complex code to charge ${amount} to the debit card.
      break;
    case 'cash':
      // Complex code to put ${amount} into the cash drawer.
      break;
  }
};
const customers = [
  {
    amount: 75.00,
    paymentMethod: 'credit-card',
  },
  {
    amount: 50.00,
    paymentMethod: 'debit-card',
  },
  {
    amount: 25.00,
    paymentMethod: 'cash',
  },
];
customers.forEach(({ amount, paymentMethod }) => {
  checkout(amount, paymentMethod);
});

Строки кода для обработки различных способов оплаты, перемежающиеся оператором switch, затрудняют чтение кода. И чтобы добавить оскорбления к травме, каждый раз, когда вы хотите изменить логику для определенного метода оплаты, вы рискуете сломать два других, потому что все они находятся внутри одной функции.

class CreditCardCheckout {
  static charge(amount) {
    // Complex code to charge ${amount} to the credit card.
  }
}
class DebitCardCheckout {
  static charge(amount) {
    // Complex code to charge ${amount} to the debit card.
  }
}
class CashCheckout {
  static charge(amount) {
    // Complex code to put ${amount} into the cash drawer.
  }
}
const customers = [
  {
    amount: 75.00,
    paymentMethod: CreditCardCheckout,
  },
  {
    amount: 50.00,
    paymentMethod: DebitCardCheckout,
  },
  {
    amount: 25.00,
    paymentMethod: CashCheckout,
  },
];
customers.forEach(({ amount, paymentMethod}) => {
  paymentMethod.charge(amount);
});

Полиморфизм помогает нам избавиться от длинного оператора switch. Каждый класс отвечает ровно за один способ оплаты.

Здесь следует отметить, что даже несмотря на то, что мы успешно удалили условное выражение из нашего отредактированного кода, на самом деле мы просто сдвигаем решение о выборе метода оплаты вверх по течению. Тот, кто создает клиентов, теперь отвечает за присвоение классов способов оплаты.

Отказаться от буквальных имен переменных

Учебники по программированию завалены паршивыми именами переменных и функций отчасти потому, что примеры кода часто не нуждаются в предоставлении полного контекста, чтобы проиллюстрировать то, что они пытаются объяснить. Например:

const arr = [
  'Breakfast Cereal',
  'Candy and Snack',
  'Dairy',
  'Paper Products and Cleaning Supplies',
];
const func = (n) => {
  const i = arr.findIndex(i => i === n);
  console.log(i);
};
func('Dairy');

Этот блок кода отлично демонстрирует использование findIndex, но совершенно не подходит для реального проекта. Имена переменных ничего не говорят нам о том, почему они существуют. Читателю трудно и еще труднее изменить, когда мы не понимаем, для каких целей они служат.

Чтобы написать лучшие имена, мы должны начать с знания контекста. Существует список распространенных названий проходов в продуктовых магазинах. Имена проходов упорядочены осмысленным образом, поэтому, когда мы вызываем функцию с заданным именем прохода, мы возвращаем ее позицию в этом списке, представляющую номер прохода. Мы можем переписать код следующим образом:

const aisles = [
  'Breakfast Cereal',
  'Candy and Snack',
  'Dairy',
  'Paper Products and Cleaning Supplies',
];
const printAisleNumber = (name) => {
  const number = aisles.findIndex((aisleName) => {
    return aisleName === name;
  });
  console.log(number);
};
printAisleNumber('Dairy');

Когда вы придумываете правильное имя переменной, это показывает, что вы действительно понимаете контекст проблемы, которую решает ваш код. Целенаправленные имена переменных превратят ваш код из простых вычислительных инструкций в руководство пользователя, которое поможет читателям узнать о вашей работе.

Отучиться от Global Scope

Когда вы впервые начнете программировать, вы, вероятно, начнете с простой hello world программы. И оттуда вы научитесь писать код в одном файле и смотреть, как программа выполняет ваш код построчно сверху вниз. Вы никогда не подозреваете, что переменная, которую вы объявили в начале файла, станет недоступной где-то еще.

Идея того, что все, что вы пишете, живет внутри глобального пространства, доступного для всех, - это препятствие для эффективной работы с абстракциями. У вас нет стимула создавать абстракции, инкапсулирующие ваш код.

Я бы посоветовал забыть о глобальном масштабе. Рассматривайте каждую функцию, объект и класс как новую вселенную. Сосредоточьтесь на том, как создавать аннотации для представления ваших идей и как эти идеи взаимодействуют друг с другом.

TL;DR

  • Замените циклы функциями высшего порядка.
  • Замените условные выражения на структуры данных и полиморфизм.
  • Замените буквальные имена переменных на целенаправленные имена переменных.
  • Забудьте о существовании глобальной области

Другие советы







Уровень кодирования

Спасибо, что стали частью нашего сообщества! Подпишитесь на наш канал YouTube или присоединитесь к Интервью по программированию Skilled.dev.