Это, наверное, то, что вы никогда не видели так элегантно написано

Java

предисловие

Увидев такое название, первой реакцией было то, что «Slag Zhahui» размахивал мачете по экрану компьютера, слушая, как Гу Тяньле говорит: «Это совершенно новая версия, в которую вы никогда раньше не играли». Конечно, это не вечеринка в заголовках, так как "это совершенно новая версия, в которую ты никогда не играл", то "это брат, заходи и смотри мою статью". (Если ты не брат, пожалуйста, останься, о, о, о)

задний план

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

Что это за код?

Нечего сказать, сразу к коду:

new AbstractSpinBusiness() {
    @Override
    protected boolean handle() {
        CompanyProfile updateProfile = getProfileForUpdateConf(staff, attrMap);
        return companyProfileDao.updateCompanyProfileConf(updateProfile) > 0;
    }
}.spin(5);

Вкратце, этот абзац означает выполнение метода в handle() и установку ограничения на 5 повторных попыток.
Тогда давайте взглянем на абстрактный класс AbstractSpinBusiness.
AbstractSpinBusiness.class

import org.apache.log4j.Logger;

public abstract class AbstractSpinBusiness {

    private Logger logger = Logger.getLogger(this.getClass());

    public void spin(int times){

        for(int i = 0; i < times; i++){
            if(handle()){
                return;
            }
            logger.debug(String.format("spin重试%s", i));
        }
    }

    /**
     * 执行主体
     * @return true:执行成功,不需要重试  false:执行失败
     */
    protected abstract boolean handle();

}

Глядя на это, кажется, что это просто реализация дескриптора в AbstractSpinBusiness, и он используется в методе вращения. Может быть, внимательные студенты открыли для себя этот шаблон проектирования с помощью шаблонов. Если вы его найдете, ставьте лайк, если не можете найти, это не большая проблема, потому что я не говорю здесь о шаблонах проектирования. Конечно, паттерн проектирования шаблонов также является очень важным и превосходным способом написания, и он повсеместно используется в абстрактном бизнесе и архитектуре!

Так элегантно ли это написание?

Напомним, что когда мы впервые изучали JDBC, нам нужно было вручную получить соединение, установить параметры в PreparedStatement, а затем переупаковать объект в формат данных, который мы хотим после выполнения.После этой серии операций мы можем обнаружить, что настоящий Это только SQL, который имеет отношение к бизнесу, а остальная работа является вспомогательной работой.

Поэтому будут рождаться ORM-фреймворки, такие как MyBatis, Hibernate и т. д. Когда разработчики используют эти фреймворки, им нужно заботиться только о нашем собственном бизнесе. Например, MyBatis, нам нужно только написать SQL бизнеса и соответствующий Mapper, затем его можно интегрировать в сервис бизнеса, а другие операции инкапсулированы во фреймворк и выполняются без восприятия.

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

расширять

Вдоль этой линии мысли, используя одну и ту же формулировку в недавней оптимизации.
В настоящее время в системе есть несколько медленных SQL, сам SQL относительно прост, и в плане выполнения EXPLAIN нет проблем, но есть более простые строки. Проблема в том, что в бизнесе генерируется большое количество параметров, в результате чего поступает слишком много данных, что приводит к снижению эффективности выполнения SQL. Тогда более прямая оптимизация заключается в контроле параметров здесь и выполнении пакетной обработки. (Разумеется, упомянутые в интернете подзапросы или параметры eq_range_index_dive_limit здесь не рассматриваются)

как бы ты написал

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

import com.google.common.collect.Lists;

import java.util.List;

public class Test {

    public static final int PARTITION_SIZE = 1000;
    
    public static void main(String[] args) {
        
        // 这里就模拟是业务参数
        List<Long> paramIds = Lists.newArrayList(1L,2L,3L);
        // 进行分隔
        List<List<Long>> partitionParamIds = Lists.partition(paramIds, PARTITION_SIZE);
        List<Object> resultList = Lists.newArrayListWithExpectedSize(paramIds.size());
        partitionParamIds.forEach(partition -> {
            // 执行具体的DAO操作,当然这里也是模拟
            resultList.addAll(new ObjectDao().getList(partition));
        });
        System.out.println(resultList.size());
    }
    
}

class ObjectDao {
    
    // 都说了是模拟模拟,不要挑刺了
    public List<Object> getList(List<Long> paramIds) {
        List<Object> resultList = Lists.newArrayList();
        for (Long paramId : paramIds) {
            resultList.add(paramId);
        }
        return resultList;
    }
    
}

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

import com.google.common.collect.Lists;

import java.util.List;

public class Test {

    public static void main(String[] args) {

        // 这里就模拟是业务参数
        List<Long> paramIds = Lists.newArrayList(1L,2L,3L);
        List<Object> resultList = new CommonDoPartition<>().partitionToQuery(paramIds,
                partition -> new ObjectDao().getList(partition));
        System.out.println(resultList.size());
    }

}

class ObjectDao {

    // 都说了是模拟模拟,不要挑刺了
    public List<Object> getList(List<Long> paramIds) {
        List<Object> resultList = Lists.newArrayList();
        for (Long paramId : paramIds) {
            resultList.add(paramId);
        }
        return resultList;
    }

}

Здесь используется класс CommonDoPartition, посмотрим, как он реализован
CommonDoPartition.java

import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;

import java.util.List;
import java.util.function.Function;

public class CommonDoPartition<T> {

    private final static Logger logger = Logger.getLogger(CommonDoPartition.class);

    public static final int PARTITION_SIZE = 1000;

    public <T, R> List<R> partitionToQuery(int partitionSize, List<T> all, Function<List<T>, List<R>> function) {
        if (CollectionUtils.isEmpty(all)) {
            logger.warn("no data to query");
            return Lists.newArrayList();
        }

        List<List<T>> partitions = Lists.partition(all, partitionSize);
        List<R> result = Lists.newArrayList();
        for (List<T> list : partitions) {
            List<R> resultList = function.apply(list);
            if (!CollectionUtils.isEmpty(resultList)) {
                result.addAll(resultList);
            }
        }
        return result;
    }

    public <T, R> List<R> partitionToQuery(List<T> all, Function<List<T>, List<R>> function) {
        return this.partitionToQuery(PARTITION_SIZE, all, function);
    }

}

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

Я буду расширять его снова.

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

    public void partitionToDo(int partitionSize, List<T> all, Consumer<List<T>> consumer) {
        if (CollectionUtils.isEmpty(all)) {
            logger.warn("no data to consume");
            return;
        }

        List<List<T>> partitions = Lists.partition(all, partitionSize);
        for (List<T> list : partitions) {
            consumer.accept(list);
        }
    }

    public void partitionToDo(List<T> all, Consumer<List<T>> consumer) {
        this.partitionToDo(PARTITION_SIZE, all, consumer);
    }
    
    
    public <T, R> void partitionToQueryAndDo(int partitionSize, List<T> all, Function<List<T>, List<R>> function, Consumer<List<R>> consumer) {
        if (CollectionUtils.isEmpty(all)) {
            logger.warn("no data to consume");
            return;
        }

        List<List<T>> partitions = Lists.partition(all, partitionSize);
        List<R> resultList;
        for (List<T> list : partitions) {
            resultList = function.apply(list);
            consumer.accept(resultList);
        }
    }

    public <T, R> void partitionToQueryAndDo(List<T> all, Function<List<T>, List<R>> function, Consumer<List<R>> consumer) {
        this.partitionToQueryAndDo(PARTITION_SIZE, all, function, consumer);
    }

Здесь partitionToDo предназначен для выполнения некоторых задач в пакетах, а partitionToQueryAndDo — для объединения предыдущих пакетов для запроса некоторых данных и работы с данными. Это все примеры, которые можно комбинировать.

конец

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