# πŸ” Code Review: ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ (SOLID, DRY, KISS) ## πŸ”΄ ΠšΡ€ΠΈΡ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠ΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ ### 1. DRY: Π”ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π° Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°Ρ… **Π€Π°ΠΉΠ»Ρ‹:** `AppearanceConfig.java`, `MessagesConfig.java`, `FiltersConfig.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** - ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ содСрТит 30-50 Π³Π΅Ρ‚Ρ‚Π΅Ρ€ΠΎΠ² с ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎΠΉ структурой - ~300 строк ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΡΡŽΡ‰Π΅Π³ΠΎΡΡ ΠΊΠΎΠ΄Π° - ΠŸΡ€ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΈ Π»ΠΎΠ³ΠΈΠΊΠΈ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π² 3 мСстах **РСшСниС:** ```java // Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ класс с generic ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ public abstract class BaseConfig { protected String getString(String path, String def) { ... } protected int getInt(String path, int def) { ... } protected boolean getBoolean(String path, boolean def) { ... } } // Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°Ρ… public class MessagesConfig extends BaseConfig { public String getNoPermission() { return getString("general.no_permission", "&#CF6679НСт ΠΏΡ€Π°Π²!"); } } ``` --- ### 2. SRP: ConfigManager Π΄Π΅Π»Π°Π΅Ρ‚ слишком ΠΌΠ½ΠΎΠ³ΠΎ **Π€Π°ΠΉΠ»:** `ConfigManager.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** - УправляСт 5 ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°ΠΌΠΈ - Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠΈΡ‚ Π»ΠΎΠ³ΠΈΠΊΡƒ обновлСния вСрсий - Π˜ΠΌΠ΅Π΅Ρ‚ 30+ Π³Π΅Ρ‚Ρ‚Π΅Ρ€ΠΎΠ² для настроСк - ΠΠ°Ρ€ΡƒΡˆΠ°Π΅Ρ‚ Single Responsibility Principle **РСшСниС:** ```java // Π Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ Π½Π° ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ классы class ConfigManager { private final AppearanceConfig appearance; private final MessagesConfig messages; // Волько ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°ΠΌΠΈ } class ConfigUpdater { // Π›ΠΎΠ³ΠΈΠΊΠ° обновлСния вСрсий void updateConfig(FileConfiguration config) { ... } } ``` --- ### 3. KISS: ColorConverter нСэффСктивСн **Π€Π°ΠΉΠ»:** `ColorConverter.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java private static String convertAmpersandColors(String message) { return message .replaceAll("&0", "") .replaceAll("&1", "") // ... 16 Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² replaceAll } ``` - ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ `replaceAll` ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠ΅Ρ‚ regex - O(n * 16) ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ - Π”ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ для `Β§` ΠΈ `&` **РСшСниС:** ```java private static final Map COLOR_MAP = Map.ofEntries( Map.entry("&0", ""), Map.entry("Β§0", ""), Map.entry("&1", ""), Map.entry("Β§1", "") // ... ); private static String convertColors(String message) { for (Map.Entry entry : COLOR_MAP.entrySet()) { message = message.replace(entry.getKey(), entry.getValue()); } return message; } ``` --- ### 4. DRY: Π”ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ permissions **Π€Π°ΠΉΠ»:** `AdvancedMessageFilter.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java if (!player.hasPermission("lochat.bypass.swear") && !player.hasPermission("lochat.bypass.filter")) if (!player.hasPermission("lochat.bypass.flood") && !player.hasPermission("lochat.bypass.filter")) if (!player.hasPermission("lochat.bypass.spam") && !player.hasPermission("lochat.bypass.filter")) ``` **РСшСниС:** ```java private boolean canBypassFilter(Player player, String filterType) { return player.hasPermission("lochat.bypass." + filterType) || player.hasPermission("lochat.bypass.filter"); } // ИспользованиС if (!canBypassFilter(player, "swear")) { FilterResult swear = swearFilter.filter(player, message); // ... } ``` --- ### 5. OCP: EnhancedChatRenderer Π½Π΅ Ρ€Π°ΡΡˆΠΈΡ€ΡΠ΅ΠΌ **Π€Π°ΠΉΠ»:** `EnhancedChatRenderer.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java switch (placeholder) { case "emoji" -> result = result.append(emoji); case "prefix" -> result = result.append(prefix); // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ плСйсхолдСра Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ измСнСния ΠΊΠΎΠ΄Π° } ``` **РСшСниС:** ```java // Strategy pattern для плСйсхолдСров interface PlaceholderResolver { Component resolve(Player player, boolean isGlobal); } Map resolvers = Map.of( "emoji", (p, g) -> buildEmojiComponent(g), "prefix", (p, g) -> buildPrefix(g), "player", (p, g) -> buildPlayerComponent(p) ); // ИспользованиС Component resolved = resolvers.getOrDefault(placeholder, (p, g) -> Component.text("{" + placeholder + "}")) .resolve(player, isGlobal); ``` --- ### 6. ISP: MuteServiceImpl Π½Π΅ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ **Π€Π°ΠΉΠ»:** `MuteServiceImpl.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java @Override public void save() { // TODO: Implement save to file } private void load() { // TODO: Implement load from file } ``` **РСшСниС:** - Π›ΠΈΠ±ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ - Π›ΠΈΠ±ΠΎ Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ интСрфСйс Π½Π° `MuteService` ΠΈ `PersistentMuteService` --- ### 7. ΠžΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΠ΅Ρ‚ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ VoiceChat **Π€Π°ΠΉΠ»:** `MuteServiceImpl.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java import de.maxhenkel.voicechat.api.BukkitVoicechatService; // ❌ НС Π½Π°ΠΉΠ΄Π΅Π½ ``` **РСшСниС:** ```gradle // build.gradle.kts dependencies { compileOnly("de.maxhenkel.voicechat:voicechat-api:2.5.0") } ``` Или ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΠΉ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒΡŽ: ```java private void initVoiceChat() { try { Class.forName("de.maxhenkel.voicechat.api.BukkitVoicechatService"); // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ } catch (ClassNotFoundException e) { plugin.getLogger().info("VoiceChat not found, voice mute disabled"); } } ``` --- ### 8. SRP: ChatEventListener слишком слоТный **Π€Π°ΠΉΠ»:** `ChatEventListener.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** - ΠœΠ΅Ρ‚ΠΎΠ΄ `onChat` содСрТит 80+ строк - ΠžΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚: Ρ€Π΅ΠΆΠΈΠΌ Ρ‡Π°Ρ‚Π°, Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΡŽ, радиус, Discord, статистику - ΠΠ°Ρ€ΡƒΡˆΠ°Π΅Ρ‚ Single Responsibility **РСшСниС:** ```java class ChatEventListener { private final ChatModeHandler modeHandler; private final MessageFilterHandler filterHandler; private final LocalChatHandler localHandler; private final DiscordHandler discordHandler; private final StatsHandler statsHandler; @EventHandler public void onChat(AsyncChatEvent event) { Player sender = event.getPlayer(); String message = extractMessage(event); // Π”Π΅Π»Π΅Π³ΠΈΡ€ΡƒΠ΅ΠΌ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ChatMode mode = modeHandler.determineMode(message); if (!filterHandler.filter(sender, message)) { event.setCancelled(true); return; } if (mode == ChatMode.LOCAL) { localHandler.handleLocal(event, sender); } discordHandler.sendToDiscord(sender, message, mode); statsHandler.recordMessage(sender, mode); } } ``` --- ### 9. DRY: Π”ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ Ρ†Π²Π΅Ρ‚ΠΎΠ² **Π€Π°ΠΉΠ»:** `ColorConverter.java` **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java private static String convertSectionColors(String message) { ... } private static String convertAmpersandColors(String message) { ... } ``` Π˜Π΄Π΅Π½Ρ‚ΠΈΡ‡Π½Π°Ρ Π»ΠΎΠ³ΠΈΠΊΠ°, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ€Π°Π·Π½Ρ‹Π΅ символы. **РСшСниС:** ```java private static String convertColors(String message, char symbol) { return message .replaceAll(symbol + "0", "") .replaceAll(symbol + "1", "") // ... } public static String convertLegacyFormats(String message) { message = convertColors(message, 'Β§'); message = convertColors(message, '&'); return message; } ``` --- ### 10. DIP: ΠŸΡ€ΡΠΌΠ°Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ ΠΎΡ‚ LoChat **Π€Π°ΠΉΠ»Ρ‹:** `EnhancedChatRenderer.java`, `ChatEventListener.java`, ΠΈ Π΄Ρ€. **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ```java com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin; ``` ΠΠ°Ρ€ΡƒΡˆΠ°Π΅Ρ‚ Dependency Inversion Principle. **РСшСниС:** ```java // Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ интСрфСйс interface ChatPlugin { ConfigManager getConfigManager(); GradientModule getGradientModule(); DiscordIntegration getDiscordIntegration(); } // LoChat Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ интСрфСйс class LoChat extends JavaPlugin implements ChatPlugin { ... } // ИспользованиС class EnhancedChatRenderer { private final ChatPlugin plugin; public EnhancedChatRenderer(ChatPlugin plugin, boolean isGlobal) { this.plugin = plugin; // НСт каста } } ``` --- ## πŸ“Š Бтатистика ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ | ΠŸΡ€ΠΈΠ½Ρ†ΠΈΠΏ | ΠΠ°Ρ€ΡƒΡˆΠ΅Π½ΠΈΠΉ | ΠšΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ | |---------|-----------|-------------| | DRY | 4 | πŸ”΄ Высокая | | SRP | 3 | πŸ”΄ Высокая | | KISS | 2 | 🟑 БрСдняя | | OCP | 1 | 🟑 БрСдняя | | DIP | 1 | 🟑 БрСдняя | | ISP | 1 | 🟒 Низкая | --- ## βœ… Π§Ρ‚ΠΎ сдСлано Ρ…ΠΎΡ€ΠΎΡˆΠΎ 1. **ServiceRegistry** - ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Π°Ρ рСализация DI 2. **Π Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π½Π° ΠΌΠΎΠ΄ΡƒΠ»ΠΈ** - gradient, integrations, core 3. **ИспользованиС интСрфСйсов** - api.service.* 4. **PluginInitializer/PluginShutdown** - Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π»ΠΎΠ³ΠΈΠΊΠΈ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ 5. **BaseConfig** - Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ класс для ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ² (ΡƒΠΆΠ΅ Π΅ΡΡ‚ΡŒ!) --- ## 🎯 ΠŸΡ€ΠΈΠΎΡ€ΠΈΡ‚Π΅Ρ‚Ρ‹ исправлСния ### Высокий ΠΏΡ€ΠΈΠΎΡ€ΠΈΡ‚Π΅Ρ‚ 1. Π˜ΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ ΠΎΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΡƒΡŽ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ VoiceChat 2. Π£ΠΏΡ€ΠΎΡΡ‚ΠΈΡ‚ΡŒ ColorConverter (KISS) 3. ВынСсти ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ permissions Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ (DRY) ### Π‘Ρ€Π΅Π΄Π½ΠΈΠΉ ΠΏΡ€ΠΈΠΎΡ€ΠΈΡ‚Π΅Ρ‚ 4. Π Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ ChatEventListener Π½Π° handler'Ρ‹ (SRP) 5. Π£Π±Ρ€Π°Ρ‚ΡŒ касты ΠΊ LoChat Ρ‡Π΅Ρ€Π΅Π· интСрфСйс (DIP) 6. Π‘Π΄Π΅Π»Π°Ρ‚ΡŒ EnhancedChatRenderer Ρ€Π°ΡΡˆΠΈΡ€ΡΠ΅ΠΌΡ‹ΠΌ (OCP) ### Низкий ΠΏΡ€ΠΈΠΎΡ€ΠΈΡ‚Π΅Ρ‚ 7. Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ save/load Π² MuteServiceImpl (ISP) 8. Π£ΠΏΡ€ΠΎΡΡ‚ΠΈΡ‚ΡŒ ConfigManager (SRP) 9. ΠžΠ±ΡŠΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ Ρ†Π²Π΅Ρ‚ΠΎΠ² (DRY) --- ## πŸ“ Π˜Ρ‚ΠΎΠ³ΠΎ ΠŸΡ€ΠΎΠ΅ΠΊΡ‚ ΠΈΠΌΠ΅Π΅Ρ‚ Ρ…ΠΎΡ€ΠΎΡˆΡƒΡŽ Π±Π°Π·ΠΎΠ²ΡƒΡŽ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ (ServiceRegistry, ΠΌΠΎΠ΄ΡƒΠ»ΠΈ, интСрфСйсы), Π½ΠΎ Π΅ΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ с: - **Π”ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΠΊΠΎΠ΄Π°** (DRY) - особСнно Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°Ρ… ΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ… - **Блишком большими классами** (SRP) - ConfigManager, ChatEventListener - **НСэффСктивными Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ°ΠΌΠΈ** (KISS) - ColorConverter Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΡŽ Π½Π°Ρ‡Π°Ρ‚ΡŒ с исправлСния критичСских ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ (VoiceChat, ColorConverter, permissions), Π·Π°Ρ‚Π΅ΠΌ постСпСнно Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΡ‚ΡŒ ΠΎΡΡ‚Π°Π»ΡŒΠ½ΠΎΠ΅.