#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. Это слишком широкий вопрос как для переполнения стека, так и для проверки кода. У меня двойная проблема: я не понимаю ваше полное приложение и не понимаю детали вашего приложения.