Путь к хорошим привычкам кодирования делает одну вещь

задняя часть

1. Предпосылки

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

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

2. Пример 1

2.1 Код проблемы

// 通过用户id判断有没有权限
func (userAuth *UserAuth) CheckPermission(uid int64, needAdminPermission bool) (hasPermission bool) {
    user, err := userAuth.GetUserInfoById(uid)
    
    // 用户存在即有权限
    if err != nil {
        hasPermission = true
    }

    // 需要管理员权限
    if needAdminPermission {
        if user.IsAdmin() {
            hasPermission = true
        } else {
            hasPermission = false
        }
    }
    
    return
}

2.2 Проблемы

Этот метод CheckPermission делает две вещи:

  • Определить, есть ли у пользователя общие права
  • Определить, есть ли у пользователя права администратора

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

  • удобочитаемость: Самый большой недостаток — плохая читаемость. Реальный код не так прост, он делает много вещей, соответственно усложняется логика и увеличивается количество строк кода, что не способствует поиску ошибок и будущим поколениям. Хуже того, методы, которые делают несколько вещей, даже не работают хорошо.

  • Гибкость и масштабируемость: Подумайте об этом, если потребности меняются, вам нужно добавить дополнительное разрешение роли, этот метод легко расширить

  • Тестируемость: Вообще говоря, чем меньше параметров и функций, тем выше тестируемость и проще написать один тест. Если бы мне нужно было написать один тест этой функции, я бы решил, какие варианты использования основаны на входных параметрах, тогда есть шесть вариантов использования для этой функции, несуществующие пользователи + правда/ложь, обычные пользователи + правда/ложь , администраторы + true/false

2.3 Оптимизация

Разделите CheckPermission на два метода: один для проверки обычных пользователей, а другой для проверки администраторов.

// 通过用户id判断有没有权限
func (userAuth *UserAuth) CheckUserPermission(uid int64) (hasPermission bool) {
    user, err := userAuth.GetUserInfoById(uid)
    // 用户存在即有权限
    if err != nil { 
        hasPermission = true
    }   
    return
}
// 通过用户id判断有没有权限
func (userAuth *UserAuth) CheckAdminPermission(uid int64) (hasPermission bool) {
    user, err := userAuth.GetUserInfoById(uid)
    // 判断是否为管理员
    if err != nil && user.isAdmin{
        hasPermission = true
    }   
    return
}

выгода:

  • Даже если мне нужно расширить еще одну роль, мне нужен только еще один метод CheckXXXPermission, который не влияет на исходный метод и одиночный тест.
  • Просто и понятно
  • Снизить риск изменений
  • Написать один тест тоже несложно. Для каждого метода CheckXXXPermission мне нужно передать только несуществующих пользователей, обычных пользователей и администраторов.Хотя количество вариантов использования не изменилось, условий комбинирования меньше, а сложность одиночного теста резко снижается.

3. Пример 2

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

3.1 Код проблемы

// 校验手机短信验证码
func (biz *SmsBiz) CheckMoileSmsCode(phone, smsCde string) (err error){
    
    // 是否需要检测图形验证码
    // 判断该手机号获取验证码的次数,超过指定次数则返回需要图形验证码的错误
    // 未超过次数,则该手机号获取验证码次数 + 1
    if err := biz.CaptchaCodeRequire(phone); err == errorutils.ErrorNeedCaptchaCode() {
        return
    }
    
    // 从缓存中获取短信验证码,然后匹配
    cacheSmsCode := biz.getSmsCodeFormCache(phone)
    if smsCode != cacheSmsCode {
        return errorutils.ErrorCodeSmsCodeNotMatch()
    }
    
    return nil
}

3.2 Проблемы

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

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

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

3.3 Оптимизация

это是否需要检测图形验证码CaptchaCodeRequire()метод с校验手机短信验证码CheckMoileSmsCode()С точки зрения абстракции он принадлежит к тому же уровню и не должен находиться вCheckMoileSmsCode()вызов и должны быть удалены.

выгода:Уменьшить скрытый вред, вызванный побочными эффектами

4. Пример 3

4.1 Код проблемы

// 对应用的名称和地址进行校验
public void checkAppNameAndUrl(String name, String url) {
    
    // 判断应用名是否为空
    StringUtils.isBlank(name);
    
    // 判断应用地址是否为空
    StringUtils.isBlank(url);
    
}

4.2 Проблемы

Не способствует повторному использованию кода

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

гипотетический спросТолько имя может быть обновлено при обновлении приложения, то этот способ неприменим

4.3 Оптимизация

4.3.1 Оптимизация 1

// 对应用的参数进行校验
public void checkAppParam(AppModel appModel) {
    
    // 判断应用名是否已存在
    checkAppName(appModel.getName());
    
    // 判断应用地址是否已存在
    checkAppUrl(appModel.getUrl());
    
}

// 检验应用名称
public void checkAppName(String name) {
    StringUtils.isBlank(name);
}

// 检验应用地址
public void checkAppUrl(String url) {
     StringUtils.isBlank(url);
}

Зная название, метод doXXX1AndXXX2 на первый взгляд неверен, лучше разбить его на doXXX1() и doXXX2()

Способствует повторному использованию кода, если мне нужно только проверить имя, а затем использовать его напрямуюcheckAppNameметод

4.3.2 Оптимизация 2

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

public class AppModel {

    private String name;
    
    private String url;
    
    // getter、setter
    
    // 对应用的参数进行校验
    public void checkParam() {
    
        // 判断应用名是否已存在
        checkName(this.getName());
    
        // 判断应用地址是否已存在
        checkUrl(this.getUrl()));
    
    }

    // 检验应用名称
    public void checkName() {
        StringUtils.isBlank(this.getName());
    }

    // 检验应用地址
    public void checkUrl() {
         StringUtils.isBlank(this.getUrl());
    }
}

Я предпочитаю этот режим перегрузки

  • Это объектно-ориентированный способ записи, а не только геттеры и сеттеры, как в режиме анемии.
  • Детали верификации экранированы извне, и для верификации нужно только вызывать извне, и не нужно разбираться, как вы верифицируете параметры. (Закон Деметры)
  • Сокращение логики бизнес-уровня

Конечно, есть и минусы, например, стоит ли делить ответственность за определение параметров на AppModel? На самом деле они относительно расплывчаты, и у разных людей разные мнения.

5. Думай

Операции CAS в атомарном пакете под Java и JUC имеют большое количество методов doXXXAndXXX(), таких как

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

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

5.1 Личное понимание

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

Можем ли мы разобрать эти два метода здесь?
Ну, я так не думаю,Поскольку операции сравнения и установки здесь выполняются атомарно, их нельзя разделить на метод сравнения и метод установки.

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

6. Резюме

  • Проблемы, которые могут возникнуть из-за нескольких обязанностей

    • Плохая читаемость
    • Плохая масштабируемость
    • Плохая тестируемость
    • Могут иметь дополнительные побочные эффекты
    • Не способствует повторному использованию метода
  • Как судить, что ответственность метода не единична

    • см. имя
    • посмотрите на уровни абстракции
    • Посмотрим, смогу ли я найти способ

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