У меня есть класс в java, который выполняет проверку, и я хочу сохранять каждый раз, когда он это делает, не создавая больше экземпляров. Как я должен это реализовать?

#java #oop

#java #ооп

Вопрос:

извините, если мой вопрос сформулирован странно, но я действительно не знаю лучшего способа описать проблему, с которой я столкнулся, не используя больше слов и не показывая свой код.

Это проблема дизайна, и сначала я объясню это более подробно, а затем предоставлю некоторый код. Моя Java-программа структурирована как таковая:

У меня есть класс ItemsFile, который по сути является классом, который считывает данные из файла, который я использую для своей программы, и возвращает список (элементов) из файла. У меня есть один, последний экземпляр класса ItemsFile в моем основном классе. У меня есть другой класс, называемый InventoryChecker, который зависит от того списка элементов, который получен из ItemsFile . Есть несколько других классов, которые зависят от экземпляра объекта ItemsFile, который у меня есть, и я передаю его в их конструкторы. Существуют также другие классы, которые зависят от InventoryChecker , экземпляр которого я также создаю в main после инициализации ItemsFile, а затем передаю его в качестве параметра.

Цель inventoryChecker в основном заключается в следующем: У него есть функция CheckInventory(inventory), которая принимает инвентаризацию и проверяет, содержит ли инвентаризация какие-либо элементы, перечисленные в ItemsFile , и если да, то этот метод возвращает true .

Проблема в следующем: у меня есть список инвентаризаций, которые я постоянно просматриваю, и когда у меня есть инвентаризация, которая заставляет CheckInventory возвращать true , он делает это постоянно и регистрирует одно и то же снова и снова, что наводняет мой файл журнала. Я хочу, чтобы он регистрировал это снова, только если в инвентаре есть что-то совсем другое, например, в нем найден другой элемент в ItemsList. На самом деле для этого не нужно читать из файла, для этого вполне нормально, если он снова регистрирует те же инвентаризации после перезапуска программы, и из-за этого в файле журнала есть дубликаты.

Итак, вот решения, о которых я думал:

1.) Я мог бы создать статическую хэш-карту внутри класса inventorychecker, которая была бы постоянной во всех экземплярах inventoryChecker, если их окажется несколько, и в основном просто добавить владельца указанного инвентаря (представьте владельца как игрока в игре, у которого может быть инвентарь,и игрок является владельцем указанного инвентаря) в качестве ключа, а элементы, которые были найдены в качестве значения (в каком-то контейнере), затем каждый раз, когда запускается метод CheckInventory, я вижу, находится ли игрок уже там, и если да, я не возвращаю true, если значениеинвентарь не изменился.

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

2.) Измените InventoryChecker так, чтобы для каждого инвентаря создавался новый объект InventoryChecker, и добавьте к нему метод compareTo, а затем создайте список где-нибудь еще, куда добавляется каждый экземпляр InventoryChecker, который вернул true . Затем, всякий раз, когда новый InventoryChecker возвращает true , я мог бы пройти цикл по этому списку и использовать метод сравнения, чтобы увидеть, равны ли какие-либо из них, и если да — я не добавляю его, но если нет — я добавляю его.

Это кажется плохим способом сделать это, потому что это создало бы тонну объектов, не говоря уже о том, что объекты InventoryChecker создаются в других классах, но inventoryChecker нужен список элементов itemFile , и это было бы еще одной проблемой, потому что тогда мне пришлось бы вставлять этот экземпляр itemFile во все классы.классы, которые используют inventoryChecker — возможно, делают их зависимыми от него, и мне просто совсем не нравится этот метод. Я действительно не знаю, есть ли какие-либо преимущества в этом методе, но я просто подумал об этом как о возможности, и, возможно, я не понял чего-то, что сделало бы этот метод хорошим.

Both of these seem to have a few problems with them, and I’m not a huge fan of either implementation.

Here’s my code as of right now:

Main.java:

 public class Main extends JavaPlugin{
    
    //filename of the yml file that contains all of the materials and quantities
    final String itemsYmlFileName = "items.yml";
    //file that stores the items to be used all throughout this plugin
    final ItemsFile itemsFile = new ItemsFile(this, itemsYmlFileName);
    
    /*
     * This function executes when the plugin is loaded/enabled,
     * due to the server starting, after a restart, or after a
     * plugin reload, etc.
     */
    @Override
    public void onEnable() {
        //this will be the inventoryChecker object passed to ContainerScanner and playerScanner. It's
        //best to get this here because it can be initialized with the itemsFile
        InventoryChecker inventoryChecker = new InventoryChecker(itemsFile);
        
        //register containerScanner event because it uses InventoryCloseEvent from bukkit.
        getServer().getPluginManager().registerEvents(new ContainerScanner(inventoryChecker), this);
        
        //Register PlayerScanner as a bukkit task so that it will execute the run() function on 
        //regular intervals. This is a synchronous task.
        @SuppressWarnings("unused")
        BukkitTask playerScan = new PlayerScanner(inventoryChecker).runTaskTimer(this, 500L, 100L);
        
        //set AddItem as the executor for this command so that it's onCommand() function is used
        //when a player types a command.
        this.getCommand("addItem").setExecutor(new AddItem(itemsFile));
    }
    
    /*
     * This function executes when the plugin is disabled due to
     * the server shutting down, restarting, reloading plugins, etc.
     */
    @Override
    public void onDisable() {
        //save any changes to the items Yml file
        itemsFile.saveFile();
    }
}
 

InventoryChecker.java:

 public class InventoryChecker {
    
    private HashMap<Material, Integer> itemList;
    
    //prevent default constructor from being used:
    @SuppressWarnings("unused")
    private InventoryChecker() {}
    
    /*
     * This constructor accepts a items file object, and will use that to read the Yml file that
     * contains all of the items and quantities to check for, and that will be used to check
     * inventories.
     */
    public InventoryChecker(ItemsFile itemsFile) {
        itemList = itemsFile.getItemsList();
    }
    
    /*
     * This constructor accepts a hashmap of Materials and Integers. The keys in this map
     * (materials) will be what the inventorychecker will check for in the inventory. The
     * values (integers) in this map will be the quantities that set off alerts. 
     */
    public InventoryChecker(HashMap<Material, Integer> items) {
        itemList = items;
    }
    
    /*
     * This function checks the inventory passed as a parameter to see if it contains any
     * materials over the set quantities, and returns true if so. The materials and quantities
     * to check for were passed during construction either as a itemsfile or a regular HashMap.
     */
    public boolean checkInventory(Inventory inv) {
        //cannot check inventory if it is null
        if(inv == null)
            return false;
        
        //tally all of the items in the inventory, and put them in the hashmap
        HashMap<Material, Integer> inventoryItems = tallyItems(inv);
        
        //loop through every entry in the itemList (hashmap of items to be checked)
        //and see if the player has any of the materials in a quantity that is 
        //greater than the value set in the config file
        for(Entry<Material, Integer> itemListEntry : itemList.entrySet()) {
            //this will be equal to null if the item is not in their inventory
            Integer i = inventoryItems.get(itemListEntry.getKey());
            
            //if it's equal to null, go on to the next item
            if(i == null)
                continue;
            
            //if an item is found, return true, they have a material in greater
            //quantity than listed in the config file.
            if(i > itemListEntry.getValue())
                return true;            
        }
        
        //return false, nothing was found
        return false;
    }
    
    /*
     * This function will accept an Inventory, and it will return a HashMap containing
     * each unique material found in the inventory as the keys, and the total amount of
     * the material in the inventory as the value:
     */
    private HashMap<Material, Integer> tallyItems(Inventory inv){
        //omitted to shorten post
    }
}
 

PlayerScanner.java (полагается на inventoryChecker, этот класс просматривает список игроков и проверяет каждый инвентарь. Это список инвентаризаций, о которых я упоминал выше, где я не хочу постоянно регистрировать одного и того же игрока снова и снова):

 public class PlayerScanner extends BukkitRunnable{
    
    private InventoryChecker inventoryChecker;
    
    //prevent usage of the default constructor, this class depends on
    //a valid InventoryChecker being passed
    @SuppressWarnings("unused")
    private PlayerScanner() {}
    
    public PlayerScanner(InventoryChecker inventoryChecker) {
        this.inventoryChecker = inventoryChecker;
    }
    
    @Override
    public void run() {
        for(Player player : Bukkit.getOnlinePlayers()) {
            //get their inventory/echest and have the inventorychecker
            //see if there are any suspicious quantities of materials in it
            boolean inv = false;
            boolean ender = false;
            inv = inventoryChecker.checkInventory(player.getInventory());
            ender = inventoryChecker.checkInventory(player.getEnderChest());
            
            if(inv || ender) {
                utility.sendAlertMessage("Player: "   player.getName()   "has illegal items in their Inventory or Echest!");
            }
        }
    }
}
 

ContainerScanner.java — другой класс, который использует inventorychecker, кроме этого, является обработчиком событий, который в основном просто запускает метод OnClose() каждый раз, когда контейнер закрывается игроком, а затем проверяет инвентарь этого контейнера:

 public class ContainerScanner implements Listener{
    
    private InventoryChecker inventoryChecker;
    
    //disable default constructor
    @SuppressWarnings("unused")
    private ContainerScanner() {}
    
    public ContainerScanner(InventoryChecker inventoryChecker) {
        this.inventoryChecker = inventoryChecker;
    }
    
    /*
     * this runs any time the InventoryCloseEvent is triggered, and it
     * scans that inventory
     */
    @EventHandler(priority = EventPriority.MONITOR)
    public void onClose(InventoryCloseEvent e) {
        
        //if it was an ender chest, don't check because ender chests will be scanned
        //regularly with the player's inventory.
        if(e.getInventory().getType().equals(InventoryType.ENDER_CHEST))
            return;
        
        //send inventory to get checked
        inventoryChecker.checkInventory(e.getInventory());
    }
}
 

Комментарии:

1. Для проверки кода существует выделенный сайт stack exchange. Это может быть хорошим местом для начала.

2. Это слишком широкий вопрос как для переполнения стека, так и для проверки кода. У меня двойная проблема: я не понимаю ваше полное приложение и не понимаю детали вашего приложения.