Skip to main content
gamedev.net
🔒 Locked

[java] Global methods in Java?

Started by Perma-noob 7 replies 16.6k views
Original Post
Perma-noob
Perma-noob
hey everyone... I've been trying to set up a GUI with Swing (using Netbeans), and I need to pass objects back and forth from between forms. I was wondering if there was a way to create an Object (like, a super-mega-object that contains references to all other objects) which can be accessed from anywhere within the project (forms, classes, etc). If thats not possible, is there another way to pass an object to another Jform? I'm getting sick of the "non-static method in static context" error message, and I can't find any examples or FAQ's online. could someone help me out?
Antheus
Antheus
Quote:
I was wondering if there was a way to create an Object (like, a super-mega-object that contains references to all other objects) which can be accessed from anywhere within the project (forms, classes, etc).


Yes, there is.

And no, I'm not telling you the name of it, since I would then need to flame you for using that aproach.

Can you post examples of how you're passing these objects around? There's not really much use for such global objects (apart from the fact that they are a design disaster). It'd be much more helpful if you show what you're trying to do, so that i can suggest a proper solution.

The static/non-static also sounds fishy. There should be one static method in the project, namely main(). Other static methods should be without context (math functions, conversion functions, and others, which don't need to hold or manipulate transient state).
Perma-noob
Perma-noob
ok, this is gonna be a long, probably meandering explanation, but here goes:

the project I'm working on will (hopefully) be used in a game (I'm leaning toward isometric RPG, mainly cuz I like Fallout and it seems like a *relatively* easy type of game for a beginner like me to make). Whatever the format, I'd like for the player to be able to travel to different villages and influence the power structure freely (without the need for hard-coded quests).

For right now, I'm working on setting up the framework for the social engine... As of right now, I have classes set up for clans, villages, trade routes, and the trade route master list. Trade routes store information on which entity (parent class of both 'village' and 'clan' classes) owes which money. Each Trade Route has a reference to both entities involved in that particular trade Route. Each clan or village adjusts it's resources based on the trade routes. (The amount of 'control' each clan has within it's home Village dictates how much of the village's resources the Clan recieves via trade routes). Recently, I've added a Jform using Swing, and set that as my main (start-up) class. This was working fine (I could view and edit each object's fields and methods through the text fields), but this was only possible because each Entity (villages and Clans) was initialized and instantiated within the main form. Now, as I add more methods, I decided to add 'create Entity' and 'create Trade Route' forms. Problem is, after I create the object in the new form, I can't refer to it from any other location. I dunno how to pass objects to forms, so I'm kinda at a loss.


if you want more detail about how the objects are set up:

masterTradeRouteList: there is only one instance of this class... it has an array of Trade Routes. New trade routes are
passed to this class after the trade route itself is created.

TradeRoute: has two Entities passed to it during construction.

Entity(both Clans and Villages) has an array of TradeRoutes in which that entity is involved (each trade route is passed to the Entity and added to the array when the trade route "signs In" to it.


I know this is long, but hopefully I clarified my problem somewhere in these incoherent ramblings.

oh yeah, if there's any other problems that you see in this setup, please lemme know... thanks again for your patience


(edited for coherence)

[Edited by - Perma-noob on June 28, 2007 8:44:44 PM]
SimonForsman
SimonForsman
the easiest way to create "global" methods in java is to use a class with only static methods.

//SomeClass.javapublic final SomeClass {    private static int value = 0;    public static void setValue(int param) {        value = param;    }    public static int getValue() {        }//OtherClass.javaSomeClass.setValue(5);int b = SomeClass.getValue();


Note: You generally don't want your static methods to have any side effects.

to store references to all other objects you can use something like

public class MyGlobals {    public static Vector<Object> objects; //All classes in Java inherit from Object, primitive types however would have to be wrapped in Classes like Integer, Float etc)}//you can then store anything in that list by doing for example:SomeClass instance = new SomeClass(54,23);MyGlobals.objects.add(instance);


You might want to rethink your design though, globals are often a very bad idea.
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Perma-noob
Perma-noob
thanks for the help. Since both repliers have advised against using globals, I'm curious how else I could pass objects to and from Jforms? I'm gonna try to clean up the code and get everything encapsulated, but maybe it's best if I get some sleep before I butcher it further. thanks for the help

-dave
RayNbow
RayNbow
If you have something like this:
public class MyFrame extends JFrame {    private someObject = null;    public MyFrame(SomeObject stuff) {        someObject = stuff;    }}


You can pass an object to your frame at creation:
SomeObject something = new SomeObject();JFrame f = new MyFrame(something);

Antheus
Antheus
In Java, you'll want to work with the usual Observer model. It makes all those problems go away.

For trade route, this would be something like this:
interface TradeRouteListener{  public void tradeRouteAdded( TradeRouteEvent event );  public void tradeRouteRemoved( TradeRouteEvent event );}class TradeRouteManager{  public void addTradeRouteListener( TradeRouteListener listener );  public void removeTradeRouteListener( TradeRouteListener listener );}


What remains is the definition of TradeRouteEvent. This one needs to describe what has happened, and needs to contain all the relevant data that fully describes that.

class TradeRouteEvent{  public TradeRouteManager source;  public Entity routeStart;  public Entity routeEnd;  ....}


How does this help you?

Let's say you want to make a route manager interface.

On creation, you pass it the TradeRouteManager instance. TradeRouteGui is TradeRouteListener, so on creation it will register itself with the manager, and receive all the updates from the manager.

class TradeRouteGui extends JForm implements TradeRouteListener{  public void tradeRouteAdded( TradeRouteEvent event )  {    // routeList is your GUI component that displays names of the routs    routeList.add( event.routeStart.getName().toString() + event.routeStart.getName().toString());  }  public void tradeRouteRemoved( TradeRouteEvent event )  {    // When a route is removed, clear it from GUI as well    routeList.remove( event.routeStart.getName().toString() + event.routeStart.getName().toString());  }  TradeRouteGui( TradeRouteManager manager )  {    routeManager = manager;    routeManager.addTradeRouteListener( this );  }  private TradeRouteManager routeManager;  ....  // when something happens in GUI, you can now manipulate the routes  void onAddRouteButtonClicked( ... ) {    routeManager.addRoute( new Route( ) );  }}The obvious problem now is, how does RouteManagerGUI know about entities, clans, etc..., when all it has is the RouteManager.Here the design of the game becomes important, so there is no general answer.One is to make your Entity, clan, ... managers statically accessible. This is bad since if will lock you into design. If you ever want to change something, you'll affect your entire codebase.Another is to define basic component hierarchy.interface GameSystem{  public String getName();  GameSystem getParent();}class AbstractGameSystem implements GameSystem{  public AbstractGameSystem( String systemName, GameSystem sparent )  {    name = systemName;    parent = sparent;  }  public String getName()  {    return name;  }  public GameSystem getParent()  {    return parent;  }  private GameSystem parent;  private String name;}interface TradeRouteSystem extends GameSystem{  public static final String TRADE_SYS_NAME = "TradeSystem";  ... getTradeRoutes() ...  ... addTradeRoute() ...  ... removeTradeRoute() ...  ... addTradeRouteListener() ...  ... removeTradeRouteListener() ...}class TradeRouteManager extends AbstractGameSystem implements TradeRouteSystem{  public  TradeRouteManager( GameSystem game )  {    super( TradeRouteSystem.TRADE_SYS_NAME, sparent );  }  // rest of the logic implementation}


Now you need a centralized game system manager.
class GameSystems extends AbstractGameSystem{  GameSystems( )  {    // no parent, this is the core game    super("Game", null);    systems = new HashMap();  }  void addSystem( GameSystem sys )  {    systems.put( sys.getName(), sys );  }  GameSystem findByName( String name )  {    return (GameSystem)systems.get( name );  }  private HashMap systems;}


So, to put all this together...

// When you start your gameGameSystems game = new GameSystems();game.addSystem( new TradeRouteManager( game ) );...game.addSystem( new ClanManager( game ) );game.addSystem( new EntityManager( game ) );...


And now, you have your game completely dynamically defined, with loose coupling, yet every aspect of the game is accessible from anywhere.

Going back to the JForm example:
class TradeRouteGui extends JForm implements TradeRouteListener{  // The rest remains the same  ....  // when something happens in GUI, you can now manipulate the routes  void onAddRouteButtonClicked( ... ) {    // Now we can get access to other systems        GameSystems gs = (GameSystems)routeManager.getParent();    ClanManager cm = (ClanManager)gs.findByName( ClanManager.CLAN_SYS_NAME );    EntityManager em = (EntityManager)gs.findByName( ClanManager.CLAN_SYS_NAME );    Entity routeStart = em... // find the entity you want    Entity routeEnd = em... // find the entity you want    // create new route    routeManager.addRoute( new Route( routeStart, routeEnd ) );  }}


There is quite some type-casting involved, but that can be taken care of - the way it's designed now is for simplicity.

What do you gain with this (obviosly there's a lot of typing involved).
- Your entire game is logically connected in one place (GameSystems class)
- While it may be tempting to make GameSystems static, you *do not* gain anything by doing that
- The architecture allows for flexible addition of new systems, without affecting existing ones
- It also allows complete rewrite of how TradeSystem works without affecting anything (remember, TradeSystem is an interface, you can have as many implementations as you want)
- Your entire game logic is tied to interfaces, and as such oblivious of implementation - only place where you know about specific implementation is when you're creating your GameSystems class.
- Your sub-parts are connected to their specific functionality (Trade GUI to TradeManager). This makes it very clear as to what that specific class does, rather than simply accessing everything globally.

Downsides:
- findByName() is theoretically slow. This is why, if you need it, you look it up once, then store the relevant systems.
- More typing upfront, double definitions (interface + implementation). Almost all of this, with exception of method definitions is easily solved through abstract classes.
- Departure from typical top-to-bottom programming style. Components are now linked through events. This affects the thinking about how to connect the components with other parts, such as GUI. But - this is how Java works, and where it's best at. Once you get comfortable with this, you'll realize how inflexible the old aproach is.

Random notes:
- Performance is a non-factor here. Aside from initial lookups, you will always be accessing sub-systems directly. And due to clean design, you'll have no duplications and no redundant couplings, making it much easier to pin-point acutal performance issues

- The listener design gives you transparent benefit of updating all interested components. Even if you have 50 open GUIs, changing one single thing will update everything that needs updating the same way as if you have a single GUI

- If you're unfamiliar with Java events (Listener, Observer, Interfaces), look them up. They make things trivial.
5MinuteGaming
5MinuteGaming
Simple Solution
You need to have a container that holds all the Entity objects and provides methods for adding, modifying, and removing them. Lets say, because I hate managers, EntityContainer. You then pass EntityContainer through the constructor of your JFrame or JDialog or even JPanel subclasses.

For instance,

//probably declared as a member variable.
private EntityContainer entities = new EntityContainer();

//most likely in one of your handlers where clanForm is a member variable
clanForm = new ClanFrame( entities );

Another Solution
You can add the Action Listeners for the forms components from within the constructor for your main frame. For instance,
MainFrame() {   super("Main Game Frame");   //... other initialization code   clanForm.getAddButton().addActionListener(        new ActionListener() {          public void actionPerformed( ActionEvent e ) {             Clan c = new Clan();             c.setName( clanForm.getClanNameComponent().getText() );             c.setMemberList( clanForm.getMemberList() );             clanList.add(c);          }       });   //... other initialization code   //you can do the same for all the components that modifies   //objects in the mainframe.}


Yet Another Solution
You might want to use a singleton dispite what people think or may say they are not always bad to use. I try to keep no more than one singlton in my program.
public final class Game {   private static final Game myGameSingleton = new Game();   //this is where you could keep all your entity objects   private ArrayList<Entity> entities = new ArrayList<Entity>();   //and your routes   private MasterTradeRouteList routeList = new MasterTradeRouteList();   protected Game() {   }     /// Main method for accessing the one and only instance.   /// It is globally accessible.   public static Game getGameSingleton() {return myGameSingleton;}   //then you would have your accessors and adding, modifying, removing methods   public void addEntity( Entity e ) {entities.add(e);)   //etc...}


And yet another Solution
You can also use the Observer pattern to provide synchronization and object passing between your forms. Your form perhaps would be an observable object and your master frame would be an observer so that your master frame would get notified whenever a change to the form occurs.

Additional Advice
A social system like the one you are describing is a bit complicated for one of your first games. Unless you are able to keep the amount of mechanics relatively small. I suggest trying something a bit simpler to get the hang of working with swing and Java guis. What about the graphics end of it? How are you intending to handle that along side the forms?

Good luck and I hope things go well for you.
Perma-noob
Perma-noob
this has got to be the greatest forum I've ever seen. you guys have been so helpful; I'm currently going through each line of code until I can understand what it does, so that may take awhile. Again, thanks for all the suggestions, and I'll keep ya posted when (if) there's any progress :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.