Вопрос Как правильно написать ConfigManager

Версия Minecraft
1.20.X

PlayGem

Пользователь
Сообщения
51
Как лучше всего написать класс, обращающийся к конфигу? Достаточно ли просто создать класс-обёртку по типу этого?

Java:
public class ConfigManager {
    private final Plugin plugin;
    
    public ConfigManager(Plugin plugin) {
        this.plugin = plugin;
    }
    
    public void save() {
      // ...
    }
    public void load() {
     // ...
    }

    public <T> get(String path, Class<T> clazz) {
    // ...
    }
}
 
Как лучше всего написать класс, обращающийся к конфигу? Достаточно ли просто создать класс-обёртку по типу этого?

Java:
public class ConfigManager {
    private final Plugin plugin;
   
    public ConfigManager(Plugin plugin) {
        this.plugin = plugin;
    }
   
    public void save() {
      // ...
    }
    public void load() {
     // ...
    }

    public <T> get(String path, Class<T> clazz) {
    // ...
    }
}
Нет, не достаточно, твой метод get будет каждый раз искать в мапе значение по указанному ключу, а также такую систему трудно рефакторить, ибо тебе нужно искать по всему проекту все вызовы get с определённым ключом и в каждом из них менять ключ.

Вместо этого минимально стоит вынести значения в поля, вытаскивать их из мапы 1 раз при загрузке конфига и получать значение из поля через геттер.
Java:
@RequiredArgsConstructor
@Getter
public class Config {
    private final Plugin plugin;
        
    private String value1;
    private int value2;
    
    public void load() {
        FileConfiguration config = plugin.getConfig();
        value1 = config.getString("value1");
        value2 = config.getInt("value2");
    }
}

Если требуется максимально простой конфиг, в котором преимущественно хранятся только строки и числа, можно использовать библиотеки которые автоматически маппят поля класса и значения конфигураций, например
 
Нет, не достаточно, твой метод get будет каждый раз искать в мапе значение по указанному ключу, а также такую систему трудно рефакторить, ибо тебе нужно искать по всему проекту все вызовы get с определённым ключом и в каждом из них менять ключ.

Вместо этого минимально стоит вынести значения в поля, вытаскивать их из мапы 1 раз при загрузке конфига и получать значение из поля через геттер.
Java:
@RequiredArgsConstructor
@Getter
public class Config {
    private final Plugin plugin;
       
    private String value1;
    private int value2;
   
    public void load() {
        FileConfiguration config = plugin.getConfig();
        value1 = config.getString("value1");
        value2 = config.getInt("value2");
    }
}

Если требуется максимально простой конфиг, в котором преимущественно хранятся только строки и числа, можно использовать библиотеки которые автоматически маппят поля класса и значения конфигураций, например
а если у меня, например сложный конфиг на дохерище значений, то как быть?
 
Я бы не рассматривал конфиг как какой либо сервис, а тем более менеджер. Это просто util/helper/loader класс.
Вот как в основном делаю это я. Нужно просто унаследовать, реализовать необходимые дефолты, создать инстанс.
Код:
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;

public abstract class AbstractConfig {

    protected final Plugin plugin;
    protected final File rawConfig;
    protected YamlConfiguration config;

    public AbstractConfig(Plugin plugin, String name) {
        this.plugin = plugin;
        this.rawConfig = new File(plugin.getDataFolder(), name + ".yml");
        boolean firstLoad = false;
        if (!rawConfig.exists()) {
            try {
                rawConfig.getParentFile().mkdirs();
                rawConfig.createNewFile();
                firstLoad = true;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        this.config = YamlConfiguration.loadConfiguration(rawConfig);

        if (firstLoad) onFirstLoad();
        checkDefault();
        save();
    }

    protected abstract void onFirstLoad();

    protected abstract void checkDefault();

    protected void setIfNotExists(String path, Object o) {
        if (!config.isSet(path)) config.set(path, o);
    }

    protected synchronized void save() {
        try {
            config.save(rawConfig);
        } catch (IOException e) {
            plugin.getLogger().log(Level.SEVERE, "Failed save config", e);
        }
    }

    public synchronized void reload() {
        this.config = YamlConfiguration.loadConfiguration(rawConfig);
        checkDefault();
        save();
    }

}
 
Я бы не рассматривал конфиг как какой либо сервис, а тем более менеджер. Это просто util/helper/loader класс.
Вот как в основном делаю это я. Нужно просто унаследовать, реализовать необходимые дефолты, создать инстанс.
Код:
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;

public abstract class AbstractConfig {

    protected final Plugin plugin;
    protected final File rawConfig;
    protected YamlConfiguration config;

    public AbstractConfig(Plugin plugin, String name) {
        this.plugin = plugin;
        this.rawConfig = new File(plugin.getDataFolder(), name + ".yml");
        boolean firstLoad = false;
        if (!rawConfig.exists()) {
            try {
                rawConfig.getParentFile().mkdirs();
                rawConfig.createNewFile();
                firstLoad = true;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        this.config = YamlConfiguration.loadConfiguration(rawConfig);

        if (firstLoad) onFirstLoad();
        checkDefault();
        save();
    }

    protected abstract void onFirstLoad();

    protected abstract void checkDefault();

    protected void setIfNotExists(String path, Object o) {
        if (!config.isSet(path)) config.set(path, o);
    }

    protected synchronized void save() {
        try {
            config.save(rawConfig);
        } catch (IOException e) {
            plugin.getLogger().log(Level.SEVERE, "Failed save config", e);
        }
    }

    public synchronized void reload() {
        this.config = YamlConfiguration.loadConfiguration(rawConfig);
        checkDefault();
        save();
    }

}
synchronized на методы конфига, зачем?
 
а если у меня, например сложный конфиг на дохерище значений, то как быть?
Сложность конфига определяется не количеством значений, а его структурой и структурой данных в нём, например легче и оптимальнее ручками прописать логику загрузки условий, по типу тех, что есть в deluxemenus, чем писать сериализеры, а простые строчки и циферки можно и автоматом
 
Последнее редактирование:
Сложность конфига определяется не количеством значений, а его структурой и структурой данных в нём, например легче и оптимальнее ручками прописать логику загрузки условий, по типу тех, что есть в deluxemenus, чем писать сериализеры, а простые строчки и циферки можно и автоматом
например
 
synchronized на методы конфига, зачем?
Это гарантирует последовательность при работе нескольких потоков. При системе где работа с методом происходит пулом потоков, каждый поток будет ждать пока другой поток отпустит тот же synchronized метод.

Практически одновременно:
Поток 1 -> save();
Поток 2 -> save();

Может произойти ситуация где поток 2 выполнит save() быстрее чем поток 1. Тем самым данные в файле могут стать некорректными.
Если требуется то лучше сам изучи тему блокировок, делать прямо как в моем примере не строго обязательно.
 
Как лучше всего написать класс, обращающийся к конфигу? Достаточно ли просто создать класс-обёртку по типу этого?

Java:
public class ConfigManager {
    private final Plugin plugin;
 
    public ConfigManager(Plugin plugin) {
        this.plugin = plugin;
    }
 
    public void save() {
      // ...
    }
    public void load() {
     // ...
    }

    public <T> get(String path, Class<T> clazz) {
    // ...
    }
}
попробуй библиотеку
оно умеет работать с bukkit/bungeecord или же velocity а ещё с standalone приложением.
 
Последнее редактирование:
Довольно давно мне зашла такая библиотека

Иногда использовал её в своих проектах. В целом прикольная и вполне удобная
 
Последнее редактирование:
Java:
public class Config {
private FileConfiguration config;
private final JavaPlugin plugin;
private final String name;
private final File configFile;


public static Config of(JavaPlugin plugin, String name) {
Config configLoader = new Config(plugin, name);
        configLoader.saveDefault();
 return configLoader;
    }

public Config(JavaPlugin plugin, String name) {
this.plugin = plugin;
this.name = name;
this.configFile = new File(plugin.getDataFolder(), name);

    }

public void saveConfig() {
 try {
this.getConfig().save(this.configFile);
} catch (IOException e) {
 throw new RuntimeException(e);
        }
    }

public void saveDefault() {


if (!configFile.exists()) {
if (plugin.getResource(name) != null) {
plugin.saveResource(name, false);
} else {
 try {
 configFile.getParentFile().mkdirs();
 configFile.createNewFile();
} catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


public FileConfiguration getConfig() {
if (this.config == null) {
 this.reloadConfig();
        }

return this.config;
    }

public void reloadConfig() {
this.config = YamlConfiguration.loadConfiguration(this.configFile);
InputStream defConfigStream = plugin.getResource(name);
if (defConfigStream != null) {
this.config.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, Charsets.UTF_8)));
        }
    }
}
сам метод класса ,а вот пример использования
Java:
  public void loadConfig() {
private Config testConfig;
testConfig = Config.of(this, "your_name.yml");
}
ну и ещё прокинуть в папке resource your_name.yml ну и там уже по настроению , делай что хочешь
 
Последнее редактирование:
Я не понимаю, во имя чего люди из раза в раз переизобретают то, что есть в баките...

Если уж мы говорим за то как ниболее просто создать недефолтные конфиги:

Java:
FileConfiguration customConfig = getFile(getDataFolder().getAbsolutePath(), "your-config-name.yml");

public FileConfiguration getFile(String path, String fileName) {
    File file = new File(path, fileName);
    if (!file.exists()) {
        plugin.saveResource(fileName, false);
    }
    return YamlConfiguration.loadConfiguration(file);
}

public void save(String path, FileConfiguration config, String fileName) { // Если вам зачем-то надо сохранить то что вы там поменяли
    try {
        config.save(new File(path, fileName));
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

А дальше просто обращаемся к нему как к обычному конфигу.

А если вам нужен обычный конфиг - используйте plugin.getConfig().get

В С Ё.

Ну а если говорить за правильный подход - уже описано в первом сообщении.
Объединено

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