1. "Вступление"
  2. "Возвращение"
  3. Одноцелевые функции
  4. "Резюме"

вступление

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

  • Возвращаюсь рано и часто
  • Одноцелевые функции

Они довольно тесно связаны и вдохновлены постом моих друзей (который вам обязательно стоит проверить) о том, что НИКОГДА не используйте ELSE.

Возвращение

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

func main() {
	data, err := loadData()

	result, err := someCalculation(data)

	return result, err
}

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

Кошмар для отладки!

Мало того, мы также будем выполнять дополнительные вычисления, которые нам не нужны!

Мы можем исправить это, проверив ошибку и вернув ее раньше.

func main() {
	data, err := loadData()

	if err != nil {
		return nil, err
	}

	result, err := someCalculation(data)

	if err != nil {
		return nil, err
	}

	return result, nil
}

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

Будет намного проще отлаживать, когда что-то пойдет не так!

Одноцелевые функции

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

Давайте возьмем следующий пример маршрутизации в JavaScript. Представьте, что мы анализируем URL, например. /:page На основе страницы импортируйте некоторый код. Мы также могли бы не устанавливать значение страницы, если кто-то переходит только на /. Мы также хотим загружать код профиля только в том случае, если пользователь аутентифицирован.

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

if (!page || page === 'home') {
  import('./home.js')
} else if (page === 'blog') {
  import('./blog.js')
} else if (page === 'login') {
  import('./login.js')
} 
if (page === 'profile' && isUserAuthenticated) {
  import('./profile.js')
} else {
  import('./lost.js')
}

Давайте разобьем его на одноцелевые функции!

Начнем с проверки, известна ли нам страница. Затем проверьте, нуждается ли страница в аутентификации и вошел ли пользователь в систему. Наконец, мы импортируем код записи в зависимости от страницы.

/**
 * Check if the page is a known page
 * Default to home page if route is just /
 * Otherwise show lost page
 * @param {String} page the page parsed from the url
 * @returns {String} validated page to go to
 */
const validatePage = (page) => {
  if (!page) {
    return 'home'
  }
  if (['profile', 'blog', 'login'].includes(page)) {
    return page
  }
  return 'lost'
}

/**
 * Check if the page is authorised and we have a user logged in
 * Otherwise, they need to login
 * @param {String} page the validated page
 * @param {Boolean} isUserAuthenticated if the user is logged in
 * @returns {String} the page to go to 
 */
const validateAuthorisedPage = (page, isUserAuthenticated) => {
  const authenticatedPages = ['profile']
  if (authenticatedPages.includes(page) && isUserAuthenticated) {
    return page
  }
  return 'login'
}

/**
 * Import the right code for each page
 * @param {String} page to load
 * @returns {Promise} the pending import
 */
const importPage = async (page) => {
  switch (page) {
    case 'home':
      return import('./home.js')
    case 'blog':
      return import('./blog.js')
    case 'profile':
      return import('./profile.js')
    case 'login':
      return import('./login.js')
    default:
      return import('./lost.js')
  }
}

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

Резюме

Таким образом, мутация — это враг!

Мысль о возвращении как можно раньше помогает сделать наш код простым, упрощает обработку ошибок и снижает вероятность возникновения побочных эффектов!

Что вы думаете? Любые другие советы для более простого кода?