предисловие
Увидев такое название, первой реакцией было то, что «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 — для объединения предыдущих пакетов для запроса некоторых данных и работы с данными. Это все примеры, которые можно комбинировать.
конец
Конечно, у меня тоже ограниченные способности, и я, возможно, не смогу абстрагировать, объединить и обобщить большинство сценариев, я просто чувствую, что в обычной разработке я только пишу бизнес и код, и мне не хватает собственного мышления и те прекрасные дизайнерские идеи и идеи.С точки зрения идей, до сих пор невозможно достичь уровня самосовершенствования. Эта статья — всего лишь небольшие размышления в моей обычной работе, а также небольшое резюме, и она предназначена только для ознакомления.