简体   繁体   English

JavaSE:使用singleton / static-only类来保存资源? 要不然是啥?

[英]JavaSE: Using a singleton/static-only class to hold resources? Or what?

Background : I'm working on a reasonably big game project to improve my OO skills. 背景 :我正在开展一个相当大的游戏项目,以提高我的OO技能。 I've been using SOLID principles and searching a lot (actually more than i code it). 我一直在使用SOLID原则并进行大量搜索(实际上比我的代码更多)。

Problem : I've been struggling with resources (sprites, to be specific). 问题 :我一直在努力寻找资源(精灵,具体而言)。 In fisrt place i made a class which would load the resources on the game(based on a string that specified where would the spritesheet be located in the jar file) and give global access to it. 在fisrt的地方,我创建了一个类,它将在游戏中加载资源(基于指定spritesheet在jar文件中的位置的字符串)并提供对它的全局访问。 It worked based on static variables. 它基于静态变量工作。 Then I read that static variables are evil for OO designs, and went to singletons. 然后我读到静态变量对OO设计是邪恶的,并且去了单身人士。 Then, i read that singletons are evil, and since it I didn't find any other option. 然后,我读到单身人士是邪恶的,因为我没有找到任何其他选择。 I understand that global state creates a lot of dependencies (in fact, I already felt it on my skin with this resource class). 我知道全局状态会产生很多依赖关系(事实上,我已经在这个资源类的皮肤上感觉到了它)。 But i don't find any good way to avoid using it. 但我没有找到任何避免使用它的好方法。

Question : On the bottom of the question you will see my implementation for the resource class and sprite class. 问题 :在问题的底部,您将看到我对资源类和精灵类的实现。 Every single class that needs do have a graphical interpretation (mobs, the player, tiles, itens and so on) depends upon the Sprite class. 每个需要的类都有图形解释(怪物,玩家,瓦片,itens等)取决于Sprite类。 And every class can get access to it by the Resource class (which only has two resources loaded in the sample, but thats offtopic). 每个类都可以通过Resource类访问它(它只在示例中加载了两个资源,但那是offtopic)。 S* o, what would be the best solution for this? S * o,对此最好的解决方案是什么? * (I would prefer if you answer with concepts, rather than give me the code done :)) *(我希望你回答概念,而不是给我完成的代码:))

OBS: I have almost the same problem with holding a database for tile types (see in the code below). OBS:我对于为磁贴类型保存数据库几乎有同样的问题(参见下面的代码)。 OBS2: Neither the database nor the resources will change during the game runtime. OBS2:在游戏运行时期间,数据库和资源都不会改变。 They are "constant", and i would only change them to add new tiles/resources on compile time. 它们是“常量”,我只会更改它们以在编译时添加新的tile /资源。 OBS2: I have a idea that might be OK, but im not sure: -Creating a superclass for sprite/resources, and then create subclass for every type of resource i need. OBS2:我有一个可能没问题的想法,但我不确定: - 为sprite / resources创建一个超类,然后为我需要的每种类型的资源创建子类。 I don't think that this solution will solve coupling issues, and it would split the sprite implementation amongst diferent packages. 我认为这个解决方案不会解决耦合问题,它会将精灵实现分成不同的包。

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);

}

} }

Have you considered using a dependency injection framework like spring or guice ? 您是否考虑过使用像springguice这样的依赖注入框架? It could inject the resource accessor class into all the places where it is used. 它可以将资源访问器类注入到使用它的所有位置。 Under the hood the resource class could behave like a singleton with the actual resource data being cached or so. 在引擎盖下,资源类可以表现得像一个单独的实际资源数据被缓存左右。 But you would not suffer from the 'evil-ness' of singletons or global state. 但你不会遭受单身人士或全球国家的“邪恶”。 You could ie define the resource class as an interface and easily mock it in unit tests. 您可以将资源类定义为接口,并在单元测试中轻松模拟它。

Quickie answer without looking too deeply at the details of this question... You could use a Guava Supplier interface , and in particular, you could use the Suppliers.ofInstance(T t ) . 快速回答而不必深入研究这个问题的细节......你可以使用Guava Supplier界面 ,特别是你可以使用Suppliers.ofInstance(T t That might be even more lightweight than Guice. 这可能比Guice更轻巧。

The plain vanilla javase solution is to pass around a reference to the Resources class. 普通的vanilla javase解决方案是传递对Resources类的引用。 typically, your classes will fall into 1 of 2 categories: larger and fewer instances, smaller and many instances. 通常,您的类将分为两类中的一类:更大和更少的实例,更小和更多实例。 for the former, you typically instantiate them with a reference to your Resources class (or, the class may already have a reference to another class which references the Resources class). 对于前者,您通常使用对Resources类的引用来实例化它们(或者,该类可能已经引用了另一个引用Resources类的类)。 for the latter, you would typically add the Resources class as a parameter on the methods that might need it (this allows to classes to be small and not have a lot of extra references). 对于后者,您通常会将Resources类添加为可能需要它的方法的参数(这允许类很小并且没有很多额外的引用)。

also, i'm assuming you would setup your Resources class so it has handles to all the resources (eg you could get sprites and tiles from it). 另外,我假设您将设置您的Resources类,因此它具有所有资源的句柄(例如,您可以从中获取精灵和图块)。

finally, you would instantiate your Resources class once at your program's entrypoint (eg main method) and pass it along to the other top-level objects which get created. 最后,您将在程序的入口点(例如main方法)实例化您的Resources类,并将其传递给创建的其他顶级对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM