JavaSE: Использование класса singleton / static-only для хранения ресурсов?Или что? - PullRequest
3 голосов
/ 23 марта 2012

Справочная информация : я работаю над довольно крупным игровым проектом, чтобы улучшить свои навыки в области ОО. Я использовал принципы SOLID и много искал (на самом деле больше, чем я его кодирую).

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

Вопрос : В нижней части вопроса вы увидите мою реализацию для класса ресурсов и класса спрайтов. Каждый отдельный класс, который нуждается в этом, имеет графическую интерпретацию (мобы, игрок, тайлы, итены и т. Д.), Зависит от класса Sprite. И каждый класс может получить доступ к нему с помощью класса Resource (у которого в образце загружено только два ресурса, но это оффтоп). S * o, что было бы лучшим решением для этого? * (Я бы предпочел, чтобы вы ответили концепциями, а не давали мне код:))

OBS: У меня почти такая же проблема с хранением базы данных для типов плиток (см. Код ниже). OBS2: ни база данных, ни ресурсы не изменятся во время игры. Они являются «постоянными», и я бы изменил их только для добавления новых плиток / ресурсов во время компиляции. OBS2: у меня есть идея, которая может быть в порядке, но я не уверен: -Создание суперкласса для спрайта / ресурсов, а затем создание подкласса для каждого типа ресурса, который мне нужен. Я не думаю, что это решение решит проблемы связывания и разделит реализацию спрайтов между различными пакетами.

public final class Resources {

private static HashMap<String, Sprite> sprites = new HashMap<String, Sprite>();

static {
    loadSprites(new SpriteSheet("spritesheets/spritesheettest.png"));
}

private static void loadSprites(SpriteSheet s) {

    sprites.put("Grass1", s.getRawSprite(0, 0).recolor(Color.GREEN));
    sprites.put("Cave1", s.getRawSprite(0, 0).recolor(Color.GRAY));

}

public static Sprite getSprite (String name) {

    return sprites.get(name);

}

}

public final class Sprite {

public static final int SIDE = 32;
private static SpriteFilter spriteFilter;
private static MySpriteRotator spriteRotator;
private BufferedImage image;

static {

    spriteFilter = new MySpriteFilter();
    spriteRotator = new MySpriteRotator();

}

public Sprite(BufferedImage img) {

    image = img;

}

public Sprite rotate (double angle, BufferedImage sprite) {


    return (spriteRotator.rotate(angle, this));

}

public Sprite recolor(Color c) {

    MySpriteFilter sf = new MySpriteFilter();
    return (spriteFilter.recolor(c, this));

}

public void render(Graphics2D g, int x, int y) {

    g.drawImage(image, x, y, null);

}

public BufferedImage getImage() {
    return image;
}

} * * тысяча двадцать-один

public final class TileDataBase {

private static HashMap<Integer, Tile> database = new HashMap<Integer, Tile>();
private static HashMap<Integer, Tile> rgbDatabase = new HashMap<Integer, Tile>();

private static final Tile grass = new MyTile(1, new Color(0, 255, 0), Resources.getSprite("Grass1"));
private static final Tile cave = new AnimatedTile(2, new Color(127, 127, 127), Resources.getSprite("Cave1"), new Animator(new Sprite[] {Resources.getSprite("Grass1"), Resources.getSprite("Cave1")}));

private TileDataBase() {
}

public static Tile getTileByID(int id, int x, int y) {

    Tile t = database.get(id).cloneTile();
    if (t == null) {

        try {
            throw new Exception("No tile for such id");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    t.setX(x);
    t.setY(y);
    return t;

}

static Tile getTileByRGB(int rgb, int x, int y) {

    Tile t = rgbDatabase.get(rgb).cloneTile();
    if (t == null) {

        try {
            throw new Exception("No tile for such rgb");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    t.setX(x * Sprite.SIDE);
    t.setY(y * Sprite.SIDE);
    return t;

}

static void putToDatabase(int id, Tile tile) {

    database.put(id, tile);

}

static void putToRGBDatabase (Color c, Tile t) {

    rgbDatabase.put(c.getRGB(), t);

}

}

Ответы [ 3 ]

0 голосов
/ 24 марта 2012

Быстрый ответ, не вдаваясь в подробности этого вопроса ... Вы можете использовать интерфейс поставщика Guava , и, в частности, вы можете использовать Suppliers.ofInstance(T t) ,Это может быть даже легче, чем Guice.

0 голосов
/ 24 марта 2012

Простое ванильное решение javase - передать ссылку на класс Resources.как правило, ваши классы делятся на 1 из 2 категорий: больше и меньше экземпляров, меньше и много экземпляров.в первом случае вы обычно создаете их экземпляры со ссылкой на ваш класс Resources (или класс может уже иметь ссылку на другой класс, который ссылается на класс Resources).для последнего вы, как правило, добавляете класс Resources в качестве параметра в методы, которые могут в нем нуждаться (это позволяет классам быть маленькими и не иметь большого количества дополнительных ссылок).

также яПредполагая, что вы настроите свой класс Resources так, чтобы он содержал все ресурсы (например, вы могли бы получать из него спрайты и тайлы).

наконец, вы можете создать экземпляр класса Resources один раз наточку входа в программу (например, метод main) и передать ее другим созданным объектам верхнего уровня.

0 голосов
/ 23 марта 2012

Рассматривали ли вы использование инфраструктуры внедрения зависимостей, например spring или guice ?Он может внедрить класс средства доступа к ресурсам во все места, где он используется.Под капотом класс ресурса может вести себя как единичный объект с фактическим кэшированием данных ресурса или около того.Но вы не пострадаете от «зла» синглетонов или глобального состояния.То есть вы можете определить класс ресурса как интерфейс и легко смоделировать его в модульных тестах.

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