Рефакторинг вашего кода с помощью анонимных функций

внешний интерфейс JavaScript API Lisp
Рефакторинг вашего кода с помощью анонимных функций

anonymous function

Анонимные функции впервые появились в языке LISP, а позже развились не только в функциональные языки, но и во все более и более анонимные функции в интерпретируемых и компилируемых языках.Возможно, есть более модное название — лямбда-выражение.

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

Анонимные функции имеют динамически программируемый процесс выполнения. Умное использование может сделать ваш код простым, но элегантным, гибким, но ограниченным. Что ж, официально переходим к теме этой статьи и используем анонимные функции для рефакторинга кода. По соглашению рефакторинга сначала указать на неприятный запах в коде (Bad Smell):

  • Определение длинных повторяющихся конфигураций
  • Условная фильтрация коллекций
  • Уникальные вызовы методов

Определение длинных повторяющихся конфигураций

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

{
  html: `
    <label><input type="checkbox" name="apple" /> Apple</label>
    <label><input type="checkbox" name="banana" /> Banana</label>
    <label><input type="checkbox" name="orange" /> Orange</label>
  `
}

Здесь есть три варианта. Структура трех вариантов одинакова. Если вы хотите добавить один ко всем параметрамtitle, то вы должны сделать это три раза.

{
  html: `
    <label><input type="checkbox" name="apple" title="Apple" /> Apple</label>
    <label><input type="checkbox" name="banana" title="Banana" /> Banana</label>
    <label><input type="checkbox" name="orange" title="Orange" /> Orange</label>
  `
}

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

{
  html: (function(fruits) {
    return fruits.map(it => `<label><input type="checkbox" name="${it.name}" title="${it.title}" /> ${it.title}</label>`).join('');
  }([
    {name: 'apple', title: 'Apple'},
    {name: 'banana', title: 'Banana'},
    {name: 'orange', title: 'Orange'}
  ]))
}

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

Хотя написание логики кода в конфигурации не является особенно рекомендуемой практикой, это ничто по сравнению с удобством сопровождения кода.

Условная фильтрация коллекций

Фильтрация коллекций — очень распространенное требование Предположим, есть студенческая коллекция:

let students = [  
  {name: 'Lucy', age: 20, sex: 'female'},
  {name: 'LiLei', age: 21, sex: 'male'},
  {name: 'Jim', age: 18, sex: 'male'}
]

Теперь, чтобы отфильтровать одноклассников, которым 20 лет:

function filterByAge(list, age) {  
  let filtered = [];
  for (let i = 0; i < list.length; i++) {
    if (list[i].age === age) {
      filtered.push(list[i]);
    }
  }
  return filtered;
}

И отфильтровать одноклассницу по имени ЛиЛей:

function filterByName(list, name) {  
  let filtered = [];
  for (let i = 0; i < list.length; i++) {
    if (list[i].name === name) {
      filtered.push(list[i]);
    }
  }
  return filtered;
}

Также отфильтруйте одноклассников мужского пола:

function filterBySex(list, sex) {  
  let filtered = [];
  for (let i = 0; i < list.length; i++) {
    if (list[i].sex === sex) {
      filtered.push(list[i]);
    }
  }
  return filtered;
}

Просто, когда вы думаете, что закончили и можете встатьсгибатьКогда я вытягивал талию, меня внезапно с силой оттолкнуло назад на сиденье, что помогло мне найти детскую обувь, названия которой начинались на «Л». Хоть ты и ммп~ в душе, что еще ты можешь сделать?Пиши, поэтому я добавил следующий метод:

function filterByNameStart(list, nameStart) {  
  let filtered = [];
  for (let i = 0; i < list.length; i++) {
    if (list[i].name.indexOf(nameStart) === 0) {
      filtered.push(list[i]);
    }
  }
  return filtered;
}

Итак, этот метод вызова:

filterByName(filterBySex(filterByAge(students, 21), 'male'), 'LiLei');  

Это можно понимать как нахождение ЛиЛей, 21-летнего мужчины. Что ж, читабельность не так уж и плоха.

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

Теперь мы используем анонимные функции для извлечения различных частей и позволяем вызывающей стороне фильтровать по своему усмотрению.filterОсновная логика метода заботится только о том, должен ли текущий элемент быть помещен в набор результатов, а другая логика оценки передается анонимной функции.fnсделать:

function filter(list, fn) {  
  let filtered = [];
  for (let i = 0; i < list.length; i++) {
    if (fn(list[i], i) === true) {
      filtered.push(list[i]);
    }
  }
  return filtered;
}

Приведенный выше пример будет записан так:

filter(students, function(member) {  
  return member.name === 'LiLei' && member.age === 21 && member.sex === 'male';
});

Использование метода стрелки может быть более кратким:

filter(students, member => member.name === 'LiLei' &&  
    member.age === 21 && 
    member.sex === 'male');

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

Уникальные вызовы методов

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

входящий5

  • Выполнить успешно и вернуться{status: 'success', data: 10}
  • Ошибка выполнения возвращается{status: 'failed', error: 'xxx'}

входящий[2, 3]

  • Выполнить успешно и вернуться{status: 'success', data: [{status: 'success', data: 4}, {status: 'success', data: 6}]}
  • Ошибка выполнения возвращается{status: 'success', data: [{status: 'failed', error: 'xxx'}, {status: 'failed', error: 'xxx'}]}

То есть один ввод будет выводиться в одном формате, а пакетный ввод будет выводиться в пакетном формате. Реализована следующая версия:

function multiple(inNum) {  
  if (Array.isArray(inNum)) {
    // 处理批量情况
    return {
      status: 'success',
      data: inNum.map(it => {
        if (isNaN(parseFloat(it))) {
          return {
            status: 'failed',
            error: 'The input is not a number'
          };
        } 

        return {
          status: 'success',
          data: it * 2
        }
      })
    };
  } else {
    // 处理单个情况
    if (isNaN(parseFloat(inNum))) {
      return {
        status: 'failed',
        error: 'The input is not a number'
      };
    }

    return {
      status: 'success',
      data: inNum * 2
    };
  }
}

Есть два метода, одиночный и пакетный, за исключением того, что форматы ввода и вывода разные, а остальная логика точно такая же. Если вы хотите заменить умножение на 2 на умножение на 3, вы должны поменять оба места. Давайте посмотрим, как использовать анонимные функции, чтобы избежать двух изменений:

function execute(data, fn) {  
  // 最小执行单元
  let single = it => {
    try {
      return {
        status: 'success',
        data: fn(it)
      };
    } catch (e) {
      return {
        status: 'failed',
        error: e.toString()
      }
    }
  };

  if (Array.isArray(data)) {
    return {
      status: 'success',
      data: data.map(single)
    }
  } else {
    return single(data);
  }
}

function multiple(inNum) {  
  return execute(inNum, it => {
    if (isNaN(parseFloat(it))) {
      throw new Error('The input is not a number');
    }

    return it * 2;
  });
}

СейчасexecuteЭтот метод заботится только о формате ввода и вывода и обработке ошибок и выполняет всю грязную работу;multipleМетод заботится только о конкретной реализации бизнеса и не заботится о том, является ли ввод одним элементом или массивом. Если вы хотите умножить на 3, просто изменитеmultipleметод последнийreturn. В качестве таких,executeЕго также можно повторно использовать другими методами API, которые, можно сказать, убивают двух зайцев одним выстрелом.

Суммировать

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

О рефакторинге хотелось бы сказать еще немного.Процесс рефакторинга должен быть постепенным.Когда меняешь в первый раз,можешь подумать,что это нормально,но во второй раз надо подумать,а нет ли лучшего способа чтобы достичь этого. Лучше всего, если модификация прозрачна для вызывающего абонента. Стоит позволить вызывающему абоненту сотрудничать с модификацией. Конечно, необходимо взвесить временные затраты. Твой босс наблюдает за тобой 😡

Счастливый код 😁