При создании Java-объекта, на который будут часто ссылаться на многих уровнях, лучше использовать экземпляр класса или сделать класс статическим? - PullRequest
0 голосов
/ 02 октября 2018

У меня есть Java-игра, которую я пишу, где мне нужна основная база данных архетипов юнитов.База данных - это просто класс, содержащий HashMap, в котором хранится пара десятков экземпляров классов, содержащих статистику отдельных типов модулей.Когда игра порождает новый юнит, она копирует юнит из базы данных, используя имя юнита, чтобы найти его в HashMap.Эта база данных создается один раз, когда программа запускается, и не изменяется.Я также не расширяю и не изменяю ни один из классов, хранящихся в HashMap.Он предназначен только для чтения для использования игровыми системами.

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

Я включил несколько простых кодов, чтобы проиллюстрировать различные подходы (извините, если япропустил точку с запятой или что-то еще, я быстро соединил свои примеры и все еще изучаю Java).

Метод 1) Я создаю базу данных как экземпляр класса, и каждый раз, когда я создаю новую армию или вызываюАрмейский метод, который добавляет юнит к армии, я передаю армии или методу ссылку на экземпляр базы данных в качестве параметра.Это должно быть довольно легко визуализировать.

Метод 2) Я создаю базу данных как экземпляр класса.В классе армии у меня есть статическая ссылка на экземпляр базы данных, который устанавливается один раз статическим методом в классе армии.После того, как база данных заполнена данными, вызывается статический метод, который устанавливает статическую ссылку для указания базы данных.После этого класс армии всегда сможет извлечь информацию из базы данных, просто сославшись на статическую переменную.

class database
{
    // HashMap of unit archetypes
    private HashMap<String, Unit> unitLibrary = new HashMap<String, Unit>();

    // method for storing units in HashMap
    void storeUnits(String fileName)
    {
        // load unit stats from file
        // add new Unit instances to unitLibrary
    }

    // getter method
    Unit getUnit(String unitName)
    {
        return unitLibrary.get(unitName);
    }
}

class Army
{
    // variables
    private static Database masterDatabase;
    private static boolean databaseSet = false;
    ArrayList<Unit> armyUnits = new ArrayList<Unit>();

    // method for creating static database reference
    void setReference(Database d)
    {
        // set static reference to main database if not previously set
        if (!databaseSet)
        {
            masterDatabase = d;
            databaseSet = true;
        }
    }

    // add unit to army
    void addUnit(String unitName)
    {
        armyUnits.add(masterDatabase.getUnit(unitName);
    }   
}

public class CodeTest
{
    public static void main(String[] args)
    {
        // create master database
        Database masterDatabase = new Database();
        masterDatabase.storeUnits("FileOfUnits.game");

        // set static reference in army class to point to master database
        Army.setReference(masterDatabase);

        // Create army
        Army army1 = new Army();
        army1.addUnit("Soldier");
    }
}

Метод 3) Я создаю HashMap в классе базы данных как статический и использую статический методзаполнить его данными.Методы getter в классе базы данных также становятся статическими.Теперь нет передачи ссылок вообще, потому что каждый раз, когда экземпляр класса армии должен извлечь из базы данных, он просто запускает Database.getUnit ().

class database
{
    // HashMap of unit archetypes
    private static HashMap<String, Unit> unitLibrary = new HashMap<String, Unit>();

    // method for storing units in HashMap
    static void storeUnits(String fileName)
    {
        // load unit stats from file
        // add new Unit instances to unitLibrary
    }

    // getter method
    static Unit getUnit(String unitName)
    {
        return unitLibrary.get(unitName);
    }
}

class Army
{
    ArrayList<Unit> armyUnits = new ArrayList<Unit>();

    // add unit to army
    void addUnit(String unitName)
    {
        armyUnits.add(Database.getUnit(unitName);
    }   
}

public class CodeTest
{
    public static void main(String[] args)
    {
        // prepare master database
        Database.storeUnits();

        // create army
        Army army2 = new army2();
        army2.add("Soldier");
    }
}

Я пробовал все три метода,и все они работают, но код все еще находится в зачаточном состоянии, и я не хочу сталкиваться с хитрыми ошибками в будущем из-за моей ранней архитектуры.Я понимаю, что он не инкапсулирован традиционным объектно-ориентированным способом, но метод 3 на самом деле кажется мне наиболее чистым, потому что база данных создается в фоновом режиме и никогда не передавалась через кучу различных вызовов методов для доступа.В коде также меньше строк и нет хитростей, как в методе 2.

Есть предложения или опыт с чем-то вроде этого?Я много сделал с Python, но только недавно начал изучать Java и все еще пытаюсь привыкнуть к ограничениям инкапсуляции.

Ответы [ 3 ]

0 голосов
/ 02 октября 2018

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

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

FileOfUnits.game как другие значения конфигурации, которые у вас будут, я бы использовал файл configuration.properties

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

Использовать некоторый элемент управленияВерсии и коммит / push с полезными комментариями, будет легче понять, почему вы сделали некоторые изменения, через несколько дней вы не вспомните, почему вы написали эти строки.

Я рекомендую вам прочитать и проверить вВ следующем порядке книги: Эффективная Java и Чистый код

0 голосов
/ 02 октября 2018

Метод 1 самый лучший.

, но метод 3 на самом деле кажется мне наиболее чистым, потому что база данных создается в фоновом режиме и никогда не передавалась через кучу различных вызовов методов для доступа

Похоже, суть проблемы для меня.Вы пытаетесь решить внедрение зависимостей со статическими переменными, и это ужасная идея.Такие фреймворки, как Spring, могут избавить вас от необходимости передавать объекты по цепочке с помощью таких функций, как autowiring , если вы считаете, что это проблема.

Что если в вашем втором примере я создамАрмия и забыть установить базу данных?У меня есть объект, который фактически находится в полусозданном состоянии.Объекты никогда не должны быть в таком состоянии.Если бы вы потребовали, чтобы он был передан конструктору, вы бы избежали этой потенциальной ошибки.

Существует проблема технического обслуживания и с вашими вторым и третьим примерами, поскольку они ограничивают вас одним экземпляром, когдаВы не представляете, как могут измениться ваши требования.Откуда вы знаете, что две разные армии будут всегда совместно использовать базу данных?Что если в будущем вам понадобятся ModernUSArmy и AncientMacedonianArmy - знаете ли вы наверняка, что они будут иметь одни и те же архетипы?

0 голосов
/ 02 октября 2018

Во-первых, учтите, что HashMap не является поточно-ориентированным.Вы не можете добавлять новые единицы в базу данных, пока клиенты запрашивают ссылки.Во-вторых, у меня был бы интерфейс базы данных и класс DatabaseImpl.Затем введите экземпляр DatabaseImpl через конструктор или установщик модуля.Основной причиной этого является тестируемость, а вы легко можете издеваться над базой данных.С точки зрения управления памятью, статические объекты будут автоматически создаваться первыми при загрузке класса, но сборщик мусора через некоторое время переведет базу данных в старое поколение, поэтому я не думаю, что это имеет большое значение с точки зрения производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...