*(и мне всё равно, что тут нет Markdown)*
# Мишань, всё херня, давай по-новой.
У статьи две проблемы: большой упор на производительность и очень неудачные примеры, которые реального новичка могут привести совсем не в ту сторону. По себе знаю, потому что программировать начинал именно с написания плагинов.
При этом советы из статьи не то, что бы плохие. Но подача очень хромает.
## 1. Некорректная работа с конфигурацией
### а) Код из примера не идентичен по функционалу
Переменная `message` из "правильного" кода будет взята единожды и переиспользоваться даже после выполнения перезагрузки конфига `plugin.reloadConfig()` - то есть игрок будет всё время видеть одно и то же сообщение. Если действительно хотим показать что-то правильное, нужно расширять скоуп. Что-нибудь вроде:
```java
public class MyClass {
private String message;
public MyClass(Plugin plugin) {
reload(plugin);
Bukkit.getScheduller().runTaskTimer(plugin, () -> {
Player player = Bukkit.getPlayer(nickname);
player.sendMessage(message);
}, 200L, 200L);
}
public void reload(Plugin plugin) {
this.message = plugin.getConfig().getString("message");
}
}
```
В следствии, при вызове `MyClass#reload` у нас наконец будет обновляться соответствующее сообщение.
Причём, далее есть примеры с переменными класса, но комментарии к ним дают совершенно другой контекст, внезапно заговаривая про рекорды. Будто бы тут больше подошёл поэтапный формат "проблема > решение > проблема решения > новое решение"
### б) Неэффективность преувеличена
Когда как для часто-вызываемых блоков кода рассказ про производительность имеет смысл, здесь его уже почти нет. Единоразовая перезагрузка конфига не может уронить ТПС сервера, если только происходит что-то совсем не так.
Тем не менее, использование секций - хорошая практика, и я бы заострил внимание именно на этом. Да даже просто из-за риска очепяток.
Единственный случай, когда здесь производительность может иметь какое-то значение - если какие-то данные активно подхватываются с конфигов. Какая-нибудь там валюта игрока. Однако даже в таком случае основной проблемой будет скорей работа с файловой системой (можно было бы плавно перейти к асинхрону), нежели какой-то просчёт хэша строк.
## 2. Страх перед локальными переменными/константами и прочим
Тут уже получше...
### Ситуация 1) ...но у новичка нет контекста, что должно быть константой
Например, есть потоко-зависимые классы, которые таковыми не кажутся. Пускай какой-нибудь `SimpleDateFormat` уже считается крайне устаревшим, но вот какой-нибудь `new DecimalFormat("#.##")` выглядит как хороший кандидат... а потом оказывается, что у него есть внутренний буфер, который может упороться при выполнении в потоках.
Также можно предположить, что при работе с SQL какой-нибудь `PreparedStatement` можно сохранить - ведь даже название на это будто намекает - но пойдёшь лесом на втором же прогоне.
Сложно придумать, как получше это всё уместить - возможно, просто написать популярные кейсы, и в остальном посоветовать обращаться к JavaDoc'ам и гуглить.
### Ситуация 2) ...но пример слишком конкретный
Как и в первом, рассматривается очень определённый кейс, из чего можно только подчеркнуть, что общие операция со строками следует делать сразу. По факту же, стоит расширить описание - мол, это касается всех подобных кейсов. Собственно, у таких значений даже название есть - инвариант цикла - т.е. то, что не меняется из итерации в итерацию. Тут же следует сделать оговорку про мутабельность:
```java
List<String> bestPeople = new ArrayList<>(getAdminNames());
for (Player player : Bukkit.getOnlinePlayers()) {
bestPeople.add(player.getName());
player.sendMessage("В список лучших людей на сервере входят: " + bestPeople); // С каждой итерацией будут добавляться новые игроки
}
```
## 3. Проблемы с коллекциями
Тоже неплохо...
### А) ...но стоит подправить пример
Тут чем-то похоже на проблему из самого первого пункта. Я *очень* часто видел, как люди создают новый `HashSet` прямо перед работой с ним. Следует помнить, что создание такого объекта, как `HashSet`, тоже небесплатно, и, по возможности, это следует делать заранее.
### Б) ...но стоит, по-моему, расширить
Хранение `Player` - далеко не единственная причина утечек. Одна из самых популярных у новичков - хранение мутабельных объектов в `HashSet` и в ключах `HashMap`. Коллекция запоминает старый хэш, а поиск/удаление идёт изменённому - объект становится недоступен, но остаётся в памяти. Такое часто видел с объектами типа `Location`.
Конечно, если всё делать аккуратно, как и в случае в `Player`, проблем можно избежать. Но лучше просто избегать таких ситуаций. Например, с локацией можно оформить свой рекорд:
```java
public record MyLocation(String world, int x, int y, int z) {
public MyLocation(Location loc) {
this(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
}
}
```
### В) ...и всё
Могу лишь придраться, что для `ImmutableList`/`ImmutableSet` в ванильной джаве уже есть свои аналоги.
## 4. Асинхрон/конкурентность и что там должно и не должно быть
Очень странные примеры. Из них кажется, будто для работы с `ConcurrentHashMap` нужно обязательно работать в асинхроне - в реальности же, в однопоточном режиме производительность `CHM` сопоставима со стандартным `HashMap`. Лучше уж сделать по примеру про использование `HashMap` vs `CHM`, и по примеру для вызова долгих или тяжёлых операций, вроде обращения по сети, нормальная компутация, или работа с файловой системой.
## 5. Принцип DRY и ради бога - следуйте ему.
Ну тут правда. Хотелось бы только блоки логики побольше, чтоб реально показать разницу.
## 6. Мелкие придирки к бесящим вещам
Тут просто оставлю пару комментариев.
### А) Dependency Injection - круто
Единственное - про громоздкость можно поспорить. При правильной архитектуре и это решаемо, но это уже совсем другая история.
### Б) Хе-хе-хе, мой старый код
Наверно, один из лучших вариантов для баланса между производительностью и поддерживаемостью кода. Но примерно на том же уровне можно сделать всё и без регулярок, и одним методом.
# Моё заключение
1. Самое важное - нужны правки примеров (а ещё `getScheduler()` с одной `l`).
2. Некоторые темы плохо раскрыты.
3. Слишком большой упор на производительность - пишешь верно, но новичка так можно довести до того, что тот постоянно будет заниматься преждевременной ~~эяк~~оптимизацией.
4. Если реально заниматься оптимизацией - без тестов многое подтвердить просто невозможно, хорошо бы чего про проверки через JMH и spark тогда.
5. Как финальный совет, можно было бы попросить новичков забить на Spigot API и перейти на Paper API. Потихоньку оно будет становиться всё более несовместимым, а фич там намного больше.