Java, XML: extending methods... and JDOM to construct applications in a flexible way?

439 views Asked by At

As a learning experiment i have been working on creating a 2d java game using LibGDX. I have a REALLY big problem with hardcoding but iv never been able to do anything about it. until recently when i (finally) figured out how to use sax/dom effectively, then found JDOM. I started to understand that with a good API like JDOM, it would be very possible to implement a user made "Object" (named say... "GameObject") in every class that gets used. this object would have methods that can construct any of the objects that are included in the program. as well as an ArrayList which would hold all of the objects created... the problem i am running into is that i cant extend from multiple classes, so i have no way to take the `Actor' class from scene2d and extend it so that it can parse the JDOM document, and fill its arraylist...

my question is this: is there a way to completely remove the hardcoding of object structures by having them defined in an xml file, such that object data that would be hardcoded can be altered at runtime)

here is some example code... its probably not perfect but it will hopefully get the idea across

GameObject... this is the class i want EVERY object to inherit from

package rastek.thegame;

import java.util.ArrayList;
import java.util.List;
import org.jdom2.Element;

public class GameObject
{
    Element node;
    ArrayList<GameObject> gameObjects;

    public GameObject(Element node)
    {
        this.setNode(node);
        this.parseJDOM();
    }

    public void parseJDOM()
    {
        List objectList = this.getNode().getChildren();
        for (int i = 0; i < objectList.size(); i++)
        {
            Element node = (Element) objectList.get(i);

            // ONE list of EVERY object used,
            if (node.getName() == "GameScreen")
            {
            //the idea being that since every onject extends gameobject, 
            //they would all take an element as an argument and automatically 
            //create its array structure with it
                this.getGameObjects().add(new GameScreen(node));
            }

//          if (node.getName() == "GameActor")
//          {
//              this.getGameObjects().add(new GameActor(node));
//          }


        }
    }

    public Element getNode()
    {
        return node;
    }

    public void setNode(Element node)
    {
        this.node = node;
    }

    public ArrayList<GameObject> getGameObjects()
    {
        return gameObjects;
    }

    public void setGameObjects(ArrayList<GameObject> gameObjects)
    {
        this.gameObjects = gameObjects;
    }
}

GameCore... the entrypoint for the libgdx abstraction layer-- the idea works here

package rastek.thegame;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;



public class GameCore extends GameObject implements ApplicationListener
{
    public GameCore(Element node)
    {
        //call the super cunstructor, thus parsing the JDOM and creating 
        //(as per the linked xml), one GameScreen and adding it to the 
        //super's array named gameObjects

        //unlike the other objects constructors, this is passed a local method, which 
        //is simply a temporary solution to obtain the xml file, 

        //as i dont want platform
        //dependancy to come into play yet, i dont want to pass the element to GameCore
        //which comes from the application entry point, which can be android, 
        //ios, or desktop dependant
        super(createJDOM());
    }



    // currently a local static and (UGH) hardcoded method, may find another
    // place and implementation for this for this
    private static Element createJDOM()
    {
        Object object = null;
        Document document;
        try
        {
            SAXBuilder saxBuilder = new SAXBuilder();
            object = saxBuilder.build(Gdx.files.internal("data/xml/GameCore.xml").reader());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        if (object != null)
        {
            document = (Document) object;
            return document.getRootElement();
        }
        else
        {
            return null;
        }
    }
    @Override
    public void create()
    {
    }
    @Override
    public void resize(int width, int height)
    {
        // TODO Auto-generated method stub
    }

    @Override
    public void render()
    {
        // TODO Auto-generated method stub
    }

    @Override
    public void pause()
    {
        // TODO Auto-generated method stub
    }

    @Override
    public void resume()
    {
        // TODO Auto-generated method stub
    }

    @Override
    public void dispose()
    {
        // TODO Auto-generated method stub
    }
}

GameScreen... one of the objects i intend to use-- the idea works here... as you can see... this object required no effort to make, the super constructor is auto generated and the propagation of its arraylist begins automatically

package rastek.thegame;

import org.jdom2.Element;
import com.badlogic.gdx.Screen;

public class GameScreen extends GameObject implements Screen
{

    public GameScreen(Element node)
    {
        super(node);
    }

    @Override
    public void render(float delta)
    {
    }

    @Override
    public void resize(int width, int height)
    {
    }

    @Override
    public void show()
    {
    }

    @Override
    public void hide()
    {
    }

    @Override
    public void pause()
    {
    }

    @Override
    public void resume()
    {
    }

    @Override
    public void dispose()
    {
    }
}

hello

package rastek.thegame;
import com.badlogic.gdx.scenes.scene2d.Actor;

public class GameActor extends Actor // ,GameObject
{
    ///i cant extend GameObject on this class, and thus cant add things 
    //like texture objects, and wrappers for complex character data
}

and the xml im using... simple tester

<?xml version="1.0" encoding="UTF-8"?>
<GameCore>

    <GameScreen>
        <Actor>
            <foo>
                <bar etc="123" >
                </bar>
            </foo>
        </Actor>
    </GameScreen>

</GameCore>
1

There are 1 answers

2
Tim B On

Use composition rather than inheritance - have all your objects include the same game variable as a member variable. Perhaps specify an Interface for the getter, so:

public interface GameObjectHolder {
    GameObject getGameObject();
}

public class ExampleGO implements GameObjectHolder {

    GameObject go = new GameObject();

    @override
    GameObject getGameObject() {
        return go;
    }
}

Now you can just do getGameObject.act(), getGameObject.draw() etc

If you need callback hooks into the GameObject make it abstract and then the object containing it can implement the abstract class with an anonymous inner class:

    GameObject go = new GameObject() {
         void doStuff() {
         }
    }