Рефакторинг - оптимизируйте вашу библиотеку различными способами

внешний интерфейс GitHub JavaScript API

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

1. Введение

Недавно у меня появилось несколько дней свободного времени, и я также учусь писать более читаемый код и более простой и удобный API. Проще говоря, речь идет о рефакторинге. Сегодня я просто поделюсь им с предыдущим небольшим проектом (ecDo, приветствую всех в звездочку) метод рефакторинга API, из этого файла в моем проекте выбирается следующий код, если нет описания:ec-do-3.0.0-beta.1.jsОбъект ecDo в (для различных целей реконструкции перечислены только 1-3 репрезентативных экземпляра, а не один за другим). Если у вас есть лучший способ, вы также можете оставить свои предложения в области комментариев.

Прежде всего, скажем, что рефакторинг должен быть не рефакторингом ради рефакторинга, а рефакторингом с целью. Следующие изменения нацелены на мою первоначальную реализацию, заменяя лучшую реализацию. В основном он включает три принципа проектирования (принцип единой ответственности, принцип открытости-закрытости, принцип наименьшего знания), которые часто используются в повседневной разработке, и существует более трех принципов проектирования API. Есть еще принцип замены в стиле Ли, принцип инверсии зависимостей и т.д., но они не ощущаются в повседневной разработке, поэтому я не буду здесь больше говорить. Затем, хотя в этих словах есть «принципы», эти принципы являются лишь предложениями и указаниями, и никаким принципам не следует следовать.Что касается развития, следует ли следовать этим принципам, конкретные обстоятельства, детальный анализ.

2. Принцип единой ответственности

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

2-1.getCount

В предыдущих версиях определение этой функции было таким: вернуть наиболее часто встречающиеся элементы массива (строку) и количество вхождений.

Первоначальный план

/**
 * @description 降序返回数组(字符串)每个元素的出现次数
 * @param arr 待处理数组
 * @param rank 长度 (默认数组长度)
 * @param ranktype 排序方式(默认降序)
 */
getCount(arr, rank, ranktype) {
    let obj = {}, k, arr1 = []
    //记录每一元素出现的次数
    for (let i = 0, len = arr.length; i < len; i++) {
        k = arr[i];
        if (obj[k]) {
            obj[k]++;
        } else {
            obj[k] = 1;
        }
    }
    //保存结果{el-'元素',count-出现次数}
    for (let o in obj) {
        arr1.push({el: o, count: obj[o]});
    }
    //排序(降序)
    arr1.sort(function (n1, n2) {
        return n2.count - n1.count
    });
    //如果ranktype为1,则为升序,反转数组
    if (ranktype === 1) {
        arr1 = arr1.reverse();
    }
    let rank1 = rank || arr1.length;
    return arr1.slice(0, rank1);
},

метод вызова

//返回值:el->元素,count->次数
ecDo.getCount([1,2,3,1,2,5,2,4,1,2,6,2,1,3,2])
//默认情况,返回所有元素出现的次数
//result:[{"el":"2","count":6},{"el":"1","count":4},{"el":"3","count":2},{"el":"4","count":1},{"el":"5","count":1},{"el":"6","count":1}]


ecDo.getCount([1,2,3,1,2,5,2,4,1,2,6,2,1,3,2],3)
//传参(rank=3),只返回出现次数排序前三的
//result:[{"el":"2","count":6},{"el":"1","count":4},{"el":"3","count":2}]

ecDo.getCount([1,2,3,1,2,5,2,4,1,2,6,2,1,3,2],null,1)
//传参(ranktype=1,rank=null),升序返回所有元素出现次数
//result:[{"el":"6","count":1},{"el":"5","count":1},{"el":"4","count":1},{"el":"3","count":2},{"el":"1","count":4},{"el":"2","count":6}]

ecDo.getCount([1,2,3,1,2,5,2,4,1,2,6,2,1,3,2],3,1)
//传参(rank=3,ranktype=1),只返回出现次数排序(升序)前三的
//result:[{"el":"6","count":1},{"el":"5","count":1},{"el":"4","count":1}]

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

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

/**
 * @description 降序返回数组(字符串)每个元素的出现次数
 * @param arr
 * @return {Array}
 */
getCount(arr) {
    let obj = {}, k, arr1 = []
    //记录每一元素出现的次数
    for (let i = 0, len = arr.length; i < len; i++) {
        k = arr[i];
        if (obj[k]) {
            obj[k]++;
        } else {
            obj[k] = 1;
        }
    }
    //保存结果{el-'元素',count-出现次数}
    for (let o in obj) {
        arr1.push({el: o, count: obj[o]});
    }
    //排序(降序)
    arr1.sort(function (n1, n2) {
        return n2.count - n1.count
    });
    return arr1;
},

3. Принцип «открыто-закрыто»

3-1.checkType

checkType проверяет тип строки. Предыдущая реализация была.

/**
 * @description 检测字符串
 * @param str 待处理字符串
 * @param type 待检测的类型
 */
checkType(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;
    }
},

метод вызова

ecDo.checkType('165226226326','mobile');
//result:false

Возвращает false, так как 165226226326 не является допустимым форматом телефона. Но с этим будет проблема, если я захочу добавить какие-то правила обнаружения в будущем? Например, добавление правила пароля. Пароль может сообщать прописные и строчные буквы, цифры, точки и символы подчеркивания. Приведенное выше решение состоит в том, чтобы добавить только один случай. Это изменение нарушает принцип открытости-закрытости, и какие будут проблемы, я уже упоминал об этом, когда говорил о паттерне стратегии ранее, и повторяться здесь не буду.

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

/**
 * @description 检测字符串
 */
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 {
        /**
        * @description 检测接口
        * @param str 待处理字符串
        * @param type 待检测的类型
        */
        check(str, type){
            return rules[type]?rules[type](str):false;
        },
        /**
        * @description 添加规则扩展接口
        * @param type 规则名称
        * @param fn 处理函数
        */
        addRule(type,fn){
            rules[type]=fn;
        }
    }
})(),

метод вызова

console.log(ecDo.checkType.check('165226226326','mobile'));//false
ecDo.checkType.addRule('password',function (str) {
    return /^[-a-zA-Z0-9._]+$/.test(str);
})
console.log(ecDo.checkType.check('***asdasd654zxc','password'));//false

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

4. Принцип наименьшего знания

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

4-1.trim

Предыдущая реализация функции обрезки

/**
 * @description 大小写切换
 * @param str 待处理字符串
 * @param type 去除类型(1-所有空格  2-左右空格  3-左空格 4-右空格)
 */
trim(str, type) {
    switch (type) {
        case 1:
            return str.replace(/\s+/g, "");
        case 2:
            return str.replace(/(^\s*)|(\s*$)/g, "");
        case 3:
            return str.replace(/(^\s*)/g, "");
        case 4:
            return str.replace(/(\s*$)/g, "");
        default:
            return str;
    }
}

метод вызова

//去除所有空格
ecDo.trim('  1235asd',1);
//去除左空格
ecDo.trim('  1235 asd ',3);

Такой метод существует на всеобщее обозрение, 1, 2, 3 и 4, представляющие параметр типа, можно назвать волшебным числом, хотя разработчики знают, что это такое. Но если его используют другие люди, такой API увеличивает затраты памяти и сложность вызовов.

Чтобы решить эту проблему, метод обработки заключается в разделении API.

/**
 * @description 清除左右空格
 */
trim(str) {
    return str.replace(/(^\s*)|(\s*$)/g, "");
},
/**
 * @description 清除所有空格
 */
trimAll(str){
    return str.replace(/\s+/g, "");
},
/**
 * @description 清除左空格
 */
trimLeft(str){
    return str.replace(/(^\s*)/g, "");
},
/**
 * @description 清除右空格
 */
trimRight(str){
    return str.replace(/(\s*$)/g, "");
}

метод вызова

//去除所有空格
ecDo.trim('  123 5asd');
//去除左空格
ecDo.trimLeft('  1235 asd ');

Таким образом, API-интерфейсов больше, но стоимость памяти и вызов проще.

4-2.encryptStr

Следующие API проще в использовании и более заметны.

первоначальный план

/**
 * @description 加密字符串
 * @param str 字符串
 * @param regArr 字符格式
 * @param type 替换方式
 * @param ARepText 替换的字符(默认*)
 */
encryptStr(str, regArr, type = 0, ARepText = '*') {
    let regtext = '',
        Reg = null,
        replaceText = ARepText;
    //repeatStr是在上面定义过的(字符串循环复制),大家注意哦
    if (regArr.length === 3 && type === 0) {
        regtext = '(\\w{' + regArr[0] + '})\\w{' + regArr[1] + '}(\\w{' + regArr[2] + '})'
        Reg = new RegExp(regtext);
        let replaceCount = this.repeatStr(replaceText, regArr[1]);
        return str.replace(Reg, '$1' + replaceCount + '$2')
    }
    else if (regArr.length === 3 && type === 1) {
        regtext = '\\w{' + regArr[0] + '}(\\w{' + regArr[1] + '})\\w{' + regArr[2] + '}'
        Reg = new RegExp(regtext);
        let replaceCount1 = this.repeatStr(replaceText, regArr[0]);
        let replaceCount2 = this.repeatStr(replaceText, regArr[2]);
        return str.replace(Reg, replaceCount1 + '$1' + replaceCount2)
    }
    else if (regArr.length === 1 && type === 0) {
        regtext = '(^\\w{' + regArr[0] + '})'
        Reg = new RegExp(regtext);
        let replaceCount = this.repeatStr(replaceText, regArr[0]);
        return str.replace(Reg, replaceCount)
    }
    else if (regArr.length === 1 && type === 1) {
        regtext = '(\\w{' + regArr[0] + '}$)'
        Reg = new RegExp(regtext);
        let replaceCount = this.repeatStr(replaceText, regArr[0]);
        return str.replace(Reg, replaceCount)
    }
},

метод вызова

ecDo.encryptStr('18819322663',[3,5,3],0,'+')
//result:188+++++663
ecDo.encryptStr('18819233362',[3,5,3],1,'+')
//result:+++19233+++
ecDo.encryptStr('18819233362',[5],0)
//result:*****233362
ecDo.encryptStr('18819233362',[5],1)
//result:"188192*****"

Проблема с этим API такая же, слишком много сказочных чисел, таких как [3,5,3], 1, 0 и т.д. По сравнению с примером 4-1, эта пара требует больших затрат памяти и сложности вызова для ее использования. Даже легко закружиться. Если это чтение исходного кода, если-иначе суждение, не говоря уже о других людях, даже если я разработчик, меня одурачат.

Решение этих проблем также аналогично 4-1. Разделить API.

/**
 * @description 加密字符串
 * @param regIndex 加密位置  (开始加密的索引,结束加密的索引)
 * @param ARepText 加密的字符 (默认*)
 */
encryptStr(str, regIndex, ARepText = '*') {
    let regtext = '',
        Reg = null,
        _regIndex=regIndex.split(','),
        replaceText = ARepText;
    //repeatStr是在上面定义过的(字符串循环复制),大家注意哦
    _regIndex=_regIndex.map(item=>+item);
    regtext = '(\\w{' + _regIndex[0] + '})\\w{' + (1+_regIndex[1]-_regIndex[0]) + '}';
    Reg = new RegExp(regtext);
    let replaceCount = this.repeatStr(replaceText, (1+_regIndex[1]-_regIndex[0]));
    return str.replace(Reg, '$1' + replaceCount);
},
 /**
 * @description 不加密字符串
 * @param regIndex 不加密位置  (开始加密的索引,结束加密的索引)
 * @param ARepText 不加密的字符 (默认*)
 */
encryptUnStr(str, regIndex, ARepText = '*') {
    let regtext = '',
        Reg = null,
        _regIndex=regIndex.split(','),
        replaceText = ARepText;
    _regIndex=_regIndex.map(item=>+item);
    regtext = '(\\w{' + _regIndex[0] + '})(\\w{' + (1+_regIndex[1]-_regIndex[0]) + '})(\\w{' + (str.length-_regIndex[1]-1) + '})';
    Reg = new RegExp(regtext);
    let replaceCount1 = this.repeatStr(replaceText, _regIndex[0]);
    let replaceCount2 = this.repeatStr(replaceText, str.length-_regIndex[1]-1);
    return str.replace(Reg, replaceCount1 + '$2' + replaceCount2);
},
/**
 * @description 字符串开始位置加密
 * @param regIndex 加密长度
 * @param ARepText 加密的字符 (默认*)
 */
encryptStartStr(str,length,replaceText = '*'){
    let regtext = '(\\w{' + length + '})';
    let Reg = new RegExp(regtext);
    let replaceCount = this.repeatStr(replaceText, length);
    return str.replace(Reg, replaceCount);
},
/**
 * @description 字符串结束位置加密
 * @param regIndex 加密长度
 * @param ARepText 加密的字符 (默认*)
 */
encryptEndStr(str,length,replaceText = '*'){
    return this.encryptStartStr(str.split('').reverse().join(''),length,replaceText).split('').reverse().join('');
},

метод вызова

console.log(`加密字符 ${ecDo.encryptStr('18819233362','3,7','+')}`)
//result:188+++++362
console.log(`不加密字符 ${ecDo.encryptUnStr('18819233362','3,7','+')}`)
//result:+++19233+++
console.log(`字符串开始位置加密 ${ecDo.encryptStartStr('18819233362','4')}`)
//result:****9233362
console.log(`字符串结束位置加密 ${ecDo.encryptEndStr('18819233362','4')}`)
//result:1881923****

Результат тот же, но вызов проще, чем раньше, и запоминать много не надо.

Есть несколько примеров изменений, похожих на 4-1 и 4-2, поэтому я не буду их здесь перечислять!

4-3.cookie

Этот пример отличается от двух приведенных выше примеров.Приведенные выше два API разбивают один API на несколько для упрощения использования, но этот API объединяет несколько API в один.

/**
 * @description 设置cookie
 * @param name cookie名称
 * @param value 值
 * @param iDay 有效时间(天数)
 */
setCookie(name, value, iDay) {
    let oDate = new Date();
    oDate.setDate(oDate.getDate() + iDay);
    document.cookie = name + '=' + value + ';expires=' + oDate;
},
/**
 * @description 获取cookie
 * @param name cookie名称
 */
getCookie(name) {
    let arr = document.cookie.split('; '),arr2;
    for (let i = 0; i < arr.length; i++) {
        arr2 = arr[i].split('=');
        if (arr2[0] == name) {
            return arr2[1];
        }
    }
    return '';
},
/**
 * @description 删除cookie
 * @param name cookie名称
 */
removeCookie(name) {
    this.setCookie(name, 1, -1);
},

метод вызова

ecDo.setCookie(cookieName,'守候',1)//设置(有效时间为1天)
ecDo.getCookie(cookieName)//获取
ecDo.removeCookie(cookieName)//删除

Добавлен API

/**
 * @description 操作cookie
 * @param name cookie名称
 * @param value 值
 * @param iDay 有效时间(天数)
 */
cookie(name, value, iDay){
    if(arguments.length===1){
        return this.getCookie(name);
    }
    else{
        this.setCookie(name, value, iDay);
    }
},

метод вызова

ecDo.cookie(cookieName,'守候',1)//设置
ecDo.cookie(cookieName)//获取
ecDo.cookie(cookieName,'守候',-1)//删除(中间的值没有意义了,只要cookie天数设置了-1,就会删除。)

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

5. Оптимизация кода

5-1.checkPwdLevel

первоначальный план

/**
 * @description 检测密码强度
 */
checkPwdLevel(str) {
    let nowLv = 0;
    if (str.length < 6) {
        return nowLv
    }
    if (/[0-9]/.test(str)) {
        nowLv++
    }
    if (/[a-z]/.test(str)) {
        nowLv++
    }
    if (/[A-Z]/.test(str)) {
        nowLv++
    }
    if (/[\.|-|_]/.test(str)) {
        nowLv++
    }
    return nowLv;
},

метод вызова

console.log(ecDo.checkPwdLevel('asd188AS19663362_.'));
//4

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

/**
 * @description 检测密码强度
 */
checkPwdLevel(str) {
    let nowLv = 0;
    if (str.length < 6) {
        return nowLv
    }
    //把规则整理成数组,再进行循环判断
    let rules=[/[0-9]/,/[a-z]/,/[A-Z]/,/[\.|-|_]/];
    for(let i=0;i<rules.length;i++){
        if(rules[i].test(str)){
            nowLv++;
        }
    }
    return nowLv;
},

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

5-2.upsetArr

первоначальный план

/**
 * @description 数组顺序打乱
 * @param arr
 */
upsetArr(arr) {
    return arr.sort(() => {
         return Math.random() - 0.5
    });
},

метод вызова

ecDo.upsetArr([1,2,3,4,5,6,7,8,9]);

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

/**
 * @description 数组顺序打乱
 * @param arr
 * @return {Array.<T>}
 */
upsetArr(arr) {
    let j,_item;
    for (let i=0; i<arr.length; i++) {
        j = Math.floor(Math.random() * i);
        _item = arr[i];
        arr[i] = arr[j];
        arr[j] = _item;
    }
    return arr;
},

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

6. Резюме

Что касается рефакторинга моей собственной кодовой базы, то на этом пока все.Эти примеры лишь части,или какие-то API.Из-за цели рефакторинга методы реализации в основном одинаковые,поэтому повторять примеры не буду. требуется на гитхаб (ec-do-3.0.0-beta.1.js) Судя по вышеизложенному выглядит хорошо.Что касается файла, который я рефакторил, то сейчас это только демо.Он находится на стадии тестирования и будет продолжать улучшаться в будущем. Если у вас есть какие-либо предложения или какой API нужно добавить, добро пожаловать в область комментариев, давайте общаться и узнавать больше.

------------------------- Великолепная разделительная линия --------------------

Хотите узнать больше, обратите внимание на мой публичный аккаунт WeChat: В ожидании книжного магазина