Рефакторинг — это не полное отрицание предыдущего кода, а лучший способ написать более качественный и удобный для сопровождения код. Непрерывное стремление и обучение могут привести к большему прогрессу.
1. Введение
Я некоторое время занимаюсь front-end разработкой, в этот период времени мои требования не только доработка проекта, но и нормальное использование функций. Я также изо всех сил старался научиться писать элегантный код, повышать его производительность и удобство сопровождения.Популярным пунктом является рефакторинг. Эта статья — моя небольшая запись, и я поделюсь ею здесь. Эта статья предназначена в основном для ознакомления, примеры также просты, а подробные и сложные примеры будут написаны и опубликованы после того, как будут подходящие примеры. Если у вас есть собственное мнение о том, как писать элегантный код, код, который можно обслуживать, или у вас есть какие-либо сильные стороны в рефакторинге, дайте советы и комментарии.
О рефакторинге я собираюсь написать цикл статей, который будет время от времени обновляться, в основном по следующим схемам: рефакторинг логического хаоса, рефакторинг разделения обязанностей, рефакторинг для добавления расширяемости, рефакторинг для упрощения использования рефакторинг и рефакторинг для повторного использования кода. Перемежаются следующие принципы: принцип единой ответственности, принцип наименьшего знания, принцип открытости-закрытости. Если у вас есть хорошие идеи по рефакторингу или хорошие примеры, пожалуйста, оставьте комментарий и оставьте ценные предложения.
2. Что такое рефакторинг
Во-первых, рефакторинг — это не переписывание. Рефакторинг грубо означает использование ряда методов рефакторинга для изменения внутренней структуры проекта без ущерба для функционального использования проекта. Улучшить читаемость и ремонтопригодность в рамках проекта.
Независимо от того, что это за проект, существует итеративный процесс от простого к сложному. В этом процессе, не влияя на использование проекта, необходимо постоянно оптимизировать код для поддержания или повышения удобочитаемости и ремонтопригодности кода. Таким образом, вы можете избежать необходимости большого общения и общения в командной совместной разработке. присоединиться к развитию проекта.
3. Зачем проводить рефакторинг
Стирайте одежду, когда она грязная, чините, когда она порвалась, и выбрасывайте, когда она вам не подходит.
При постоянном росте бизнес-требований, изменений и отказа код проекта неизбежно будет иметь дефекты, которые повлияют на читабельность, ремонтопригодность кода и даже повлияют на производительность проекта. Цель рефакторинга — устранить эти дефекты и обеспечить качество и производительность кода. Но предпосылка заключается в том, что это не может повлиять на использование проекта.
Что же касается причин рефакторинга, то я их подытожил сам, и есть, наверное, следующие моменты
- Логическая структура функции хаотична, или из-за отсутствия комментариев даже автору оригинального кода сложно разобраться в логике.
- Функции вообще не имеют масштабируемости, и они не могут гибко обрабатываться при возникновении новых изменений.
- Из-за сильной связи объектов или бизнес-логики код бизнес-логики огромен, и его трудно устранить во время обслуживания.
- Слишком много повторяющегося кода, нет возможности повторного использования.
- По мере развития технологий код также может нуждаться в модификации с добавлением новых функций.
- С углублением обучения есть ли лучшее решение для предыдущего кода.
- Из-за того, как написан код, хотя функция используется нормально, она потребляет много производительности и нуждается в оптимизации путем изменения решения.
4. Когда проводить рефакторинг
в нужное время, в нужное время
В моем понимании рефакторинг можно назвать циклом разработки и сопровождения, который проходит через весь проект и может рассматриваться как часть разработки. С точки зрения непрофессионала, в любой момент разработки, если вы видите, что код неудобен и вызывает обсессивно-компульсивное расстройство, вы можете подумать о рефакторинге. Однако перед рефакторингом ознакомьтесь со следующими пунктами.
- Во-первых, рефакторинг требует времени. Это может занять больше времени, чем предыдущее время разработки.
- Во-вторых, рефакторинг заключается в оптимизации кода при условии, что это не повлияет на использование проекта.
- Наконец, сложность рефакторинга различна, она может быть лишь незначительно изменена, а сложность может быть выше, чем в предыдущей разработке.
Исходя из вышеперечисленных пунктов, нужно оценить, стоит ли проводить рефакторинг. Показатели оценки могут относиться к следующим пунктам
- Количество: требуется ли рефакторинг слишком большого количества кода.
- Качество: удобочитаемость, ремонтопригодность, сложность логики кода и другие вопросы, независимо от того, достигло ли влияние на качество кода невыносимого уровня.
- Время: достаточно ли времени для рефакторинга и тестирования.
- Эффект: если код подвергся рефакторингу, каковы улучшения, такие как улучшение качества кода, повышение производительности, улучшение поддержки последующих функций и т. д.
5. Как провести рефакторинг
Выберите цель, целевая атака
Как рефакторить, это конкретная ситуация, конкретный анализ. Например, «почему такой рефакторинг». Если вы обнаружите какие-либо проблемы с кодом, вы можете улучшить его в зависимости от ситуации.
Рефакторинг — это тоже написание кода, но речь идет не только о написании, но и о сортировке и оптимизации. Если для написания кода требуется процесс «обучение-понимание-мастерство», то рефакторинг требует процесса «обучение-восприятие-прорыв-мастерство».
В случае рефакторинга для иллюстрации используются следующие простые примеры.
5-1 Функция не расширяема
В качестве примера ниже у меня есть библиотека в одном из API
//检测字符串
//checkType('165226226326','mobile')
//result:false
let checkType=function(str, type) {
switch (type) {
case 'email':
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
case 'mobile':
return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
case 'tel':
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
case 'number':
return /^[0-9]$/.test(str);
case 'english':
return /^[a-zA-Z]+$/.test(str);
case 'text':
return /^\w+$/.test(str);
case 'chinese':
return /^[\u4E00-\u9FA5]+$/.test(str);
case 'lower':
return /^[a-z]+$/.test(str);
case 'upper':
return /^[A-Z]+$/.test(str);
default:
return true;
}
}
Этот API выглядит так, как будто нет проблем с обнаружением некоторых часто используемых данных. Но есть две проблемы.
1. Но что, если вы хотите добавить другие правила? Вы должны добавить регистр к функции. Добавьте правило и измените его один раз! Это нарушает принцип открытого-закрытого (открыто для расширения, закрыто для модификации). И это также сделает весь API раздутым и сложным в обслуживании.
2. Другая проблема заключается в том, что, например, на странице А необходимо добавить проверку суммы, а странице Б требуется проверка даты, но проверка суммы требуется только на странице А, а проверка даты требуется только на странице B. Если вы продолжаете добавлять случай . Это необходимо для того, чтобы страница A добавила правила проверки, которые требуются только на странице B, что приводит к ненужным накладным расходам. То же самое верно и для страницы B.
Предлагаемый способ - добавить расширенный интерфейс к этому API.
let checkType=(function(){
let rules={
email(str){
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
},
mobile(str){
return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
},
tel(str){
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
},
number(str){
return /^[0-9]$/.test(str);
},
english(str){
return /^[a-zA-Z]+$/.test(str);
},
text(str){
return /^\w+$/.test(str);
},
chinese(str){
return /^[\u4E00-\u9FA5]+$/.test(str);
},
lower(str){
return /^[a-z]+$/.test(str);
},
upper(str){
return /^[A-Z]+$/.test(str);
}
};
//暴露接口
return {
//校验
check(str, type){
return rules[type]?rules[type](str):false;
},
//添加规则
addRule(type,fn){
rules[type]=fn;
}
}
})();
//调用方式
//使用mobile校验规则
console.log(checkType.check('188170239','mobile'));
//添加金额校验规则
checkType.addRule('money',function (str) {
return /^[0-9]+(.[0-9]{2})?$/.test(str)
});
//使用金额校验规则
console.log(checkType.check('18.36','money'));
Приведенный выше код немного больше, но его не так уж сложно понять, и масштабируемость также доступна.
Вышеупомянутое улучшение на самом деле улучшено за счет использования режима стратегии (инкапсуляции ряда алгоритмов, так что код алгоритма и логический код могут быть независимыми друг от друга и не влиять на использование алгоритма). Концепция шаблона стратегии немного запутана для понимания, но когда вы смотрите на код, вы не должны отвлекаться.
Давайте немного расширимся здесь, с точки зрения функции, путем рефакторинга мы можем добавить расширяемость функции, которая здесь реализована. Но если вышеcheckType
проект с открытым исходным кодомAPI
, вызывающий метод перед рефакторингом:checkType('165226226326','phone')
. Способ вызова после рефакторинга: checkType.check('188170239','phone')
;илиcheckType.addRule()
;. Если автор проекта с открытым исходным кодом проводит рефакторинг указанным выше образом, то предыдущее использование проекта с открытым исходным кодомcheckType
этоAPI
Разработчики, возможна трагедия, потому что пока разработчики обновляют версию проекта, будут проблемы. Потому что реконструированные выше не имеют обратной совместимости.
Если вы хотите иметь обратную совместимость, это на самом деле не сложно. Просто добавьте приговор.
let checkType=(function(){
let rules={
email(str){
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
},
mobile(str){
return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
},
tel(str){
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
},
number(str){
return /^[0-9]$/.test(str);
},
english(str){
return /^[a-zA-Z]+$/.test(str);
},
text(str){
return /^\w+$/.test(str);
},
chinese(str){
return /^[\u4E00-\u9FA5]+$/.test(str);
},
lower(str){
return /^[a-z]+$/.test(str);
},
upper(str){
return /^[A-Z]+$/.test(str);
}
};
//暴露接口
return function (str,type){
//如果type是函数,就扩展rules,否则就是验证数据
if(type.constructor===Function){
rules[str]=type;
}
else{
return rules[type]?rules[type](str):false;
}
}
})();
console.log(checkType('188170239','mobile'));
checkType('money',function (str) {
return /^[0-9]+(.[0-9]{2})?$/.test(str)
});
//使用金额校验规则
console.log(checkType('18.36','money'));
Это может работать нормально и имеет масштабируемость, но для чистоты кода это не элегантно. потому чтоcheckType
Это нарушает принцип единой функции. Функция со слишком большим количеством обязанностей может впоследствии привести к непредвиденным проблемам, а ее использование также очень запутанно.
Столкнувшись с такой ситуацией, личное ноу-хау: сохранитьcheckType
, без всяких модификаций добавить в проект новыйAPI
,НапримерcheckTypOfString
, напишите рефакторинговый код вcheckTypOfString
в. Помогите разработчикам стать менее старыми различными способамиcheckType
, многоцелевойcheckTypOfString
. В последующих итерациях проекта откажитесь, когда это уместно.checkType
.
5-2.Функции нарушают единый принцип
Одним из самых больших последствий функций, нарушающих единый принцип, является то, что это приведет к логической путанице. Если функция берет на себя слишком много обязанностей, попробуйте: принцип единственной функции — функция делает только одну вещь.
Следующий пример
//现有一批的录入学生信息,但是数据有重复,需要把数据进行去重。然后把为空的信息,改成保密。
let students=[
{
id:1,
name:'守候',
sex:'男',
age:'',
},
{
id:2,
name:'浪迹天涯',
sex:'男',
age:''
},
{
id:1,
name:'守候',
sex:'',
age:''
},
{
id:3,
name:'鸿雁',
sex:'',
age:'20'
}
];
function handle(arr) {
//数组去重
let _arr=[],_arrIds=[];
for(let i=0;i<arr.length;i++){
if(_arrIds.indexOf(arr[i].id)===-1){
_arrIds.push(arr[i].id);
_arr.push(arr[i]);
}
}
//遍历替换
_arr.map(item=>{
for(let key in item){
if(item[key]===''){
item[key]='保密';
}
}
});
return _arr;
}
console.log(handle(students))
Проблем с ходовым результатом нет, но задумайтесь, если в будущем требования изменятся, например, в информации о студентах больше не будет дубликатов записей, а функцию дедупликации требуется убрать. В результате вся функция должна быть изменена. Это также влияет на следующие рабочие процедуры. Это равносильно изменению требований, и весь метод стоит на коленях. Пожар у городских ворот затронул Чию.
Нижеследующее использует единый принцип для построения
let handle={
removeRepeat(arr){
//数组去重
let _arr=[],_arrIds=[];
for(let i=0;i<arr.length;i++){
if(_arrIds.indexOf(arr[i].id)===-1){
_arrIds.push(arr[i].id);
_arr.push(arr[i]);
}
}
return _arr;
},
setInfo(arr){
arr.map(item=>{
for(let key in item){
if(item[key]===''){
item[key]='保密';
}
}
});
return arr;
}
};
students=handle.removeRepeat(students);
students=handle.setInfo(students);
console.log(students);
Результат тот же, но требования изменены, например, не нужно дедуплицировать, достаточно комментировать или удалять код напрямую. Это эквивалентно разделению обязанностей функции, причем обязанности не влияли друг на друга раньше. Удаление этого шага в середине не повлияет на следующий шаг.
//students=handle.removeRepeat(students);
students=handle.setInfo(students);
console.log(students);
5-3 Оптимизация написания функций
Это тот случай, когда теперь реализована лучшая реализация предыдущей функции, не влияющая на использование. Просто замените предыдущее решение лучшим решением.
Например, следующие требования были отправлены другом в группе, и некоторые обсуждения были запущены позже. дать20180408000000
Строка, функция formatDate для обработки и возврата2018-04-08 00:00:00
.
предыдущее решение
let _dete='20180408000000'
function formatStr(str){
return str.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, "$1-$2-$3 $4:$5:$6")
}
formatStr(_dete);
//"2018-04-08 00:00:00"
Позднее это решение было исследовано. Этот метод заключается в замене данных заполнения в соответствии с положением x, что несложно понять
let _dete='20180408000000'
function formatStr(str,type){
let _type=type||"xxxx-xx-xx xx:xx:xx";
for(let i = 0; i < str.length; i++){
_type = _type.replace('x', str[i]);
}
return _type;
}
formatStr(_dete);
result:"2018-04-08 00:00:00"
В ближайшие дни статья в Наггетс (Эти элегантные и духовные фрагменты кода JS, спасибо за предоставленный ценный метод), и нашел лучший метод реализации в комментариях, и я сам преобразую его в соответствии с вышеуказанными требованиями.
let _dete='20180408000000'
function formatStr(str,type){
let i = 0,_type = type||"xxxx-xx-xx xx:xx:xx";
return _type .replace(/x/g, () => str[i++])
}
formatStr(_dete);
result:"2018-04-08 00:00:00"
5-4 Повторное использование кода
Все приведенные выше примеры написаны на js, давайте поговорим о двух примерах, которые немного связаны с рендерингом данных html-vue.
В приведенном ниже кодеpayChannelEn2Cn
addZero
formatDateTime
Функции все в порядкеmethods
в. Всем обратить внимание.
предыдущее написание
<span v-if="cashType==='cash'">现金</span>
<span v-else-if="cashType==='check'">支票</span>
<span v-else-if="cashType==='draft'">汇票</span>
<span v-else-if="cashType==='zfb'">支付宝</span>
<span v-else-if="cashType==='wx_pay'">微信支付</span>
<span v-else-if="cashType==='bank_trans'">银行转账</span>
<span v-else-if="cashType==='pre_pay'">预付款</span>
Проблема с этим написанием в том, что, во-первых, много кода, а во-вторых, если в проекте 10 мест для рендеринга таких данных, если требования к рендерингу изменятся. Например, стоимость банковского перевода отbank_trans
изменить наbank
, то вы должны изменить его 10 раз в проекте. Цена времени слишком высока.
Позже я использовал следующий метод написания, который представляет собой небольшой рефакторинг.
<span>{{payChannelEn2Cn(cashType)}}</span>
payChannelEn2Cn
функция, выход
payChannelEn2Cn(tag){
let _obj = {
'cash': '现金',
'check': '支票',
'draft': '汇票',
'zfb': '支付宝',
'wx_pay': '微信支付',
'bank_trans': '银行转账',
'pre_pay': '预付款'
};
return _obj[tag];
}
Другим примером является способ записи временных меток во время. Принцип тот же, только код другой. Ниже приведен исходный код.
<span>{{new Date(payTime).toLocaleDateString().replace(/\//g, '-')}}
{{addZero(new Date(payTime).getHours())}}:
{{addZero(new Date(payTime).getMinutes())}}:
{{addZero(new Date(payTime).getSeconds())}}</span>
addZero
Функция заполнения времени
Example:3->03
addZero(i){
if (i < 10) {
i = "0" + i;
}
return i;
}
Проблема такая же, как и выше, так что я не буду здесь больше говорить, просто напишу рефакторинговый код
<span>{{formatDateTime(payTime)}} </span>
formatDateTime
функция, строка формата
formatDateTime(dateTime){
return `${new Date(payTime).toLocaleDateString().replace(/\//g, '-')} ${this.addZero(new Date(payTime).getHours())}:${this.addZero(new Date(payTime).getMinutes())}:${this.addZero(new Date(payTime).getSeconds())}`;
}
Может быть, многие люди видят это и думают, что рефакторинг — это очень просто, и правильно так думать, настолько прост рефакторинг. Однако рефакторинг также сложен, потому что рефакторинг представляет собой пошаговый процесс и требует постепенного процесса, Можно даже сказать, что рефакторинг — это небольшое изменение снова и снова, постепенно формирующее процесс качественного изменения. Как убедиться, что каждое изменение имеет смысл для улучшения кода; как убедиться, что каждое изменение не повлияет на нормальное использование проекта; если вы обнаружите, что изменение бессмысленно или если изменение делает код хуже, вы можете остановить или откат кода в любое время — сложная часть рефакторинга.
6. Резюме
Вот и все о рефакторинге.Эта статья в основном знакомит с рефакторингом, и примеры очень простые. Цель состоит в том, чтобы лучше понять некоторые концепции рефакторинга. Что касается рефакторинга, он может быть сложным, может быть простым. Как провести рефакторинг — это тоже конкретная ситуация, и стандартного ответа для конкретного анализа и рефакторинга нет. В дальнейшем, если будут хорошие примеры, я поделюсь ими как можно скорее, и дам вам конкретную ситуацию и конкретный анализ: зачем и как рефакторить.
Наконец, если у вас есть какие-либо предложения или мнения по поводу статьи, добро пожаловать на общение, обмен опытом и совместный прогресс.
------------------------- Великолепная разделительная линия --------------------
Хотите узнать больше, обратите внимание на мой публичный аккаунт WeChat: В ожидании книжного магазина