Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (492)
Games in Android Showcase (112)
games submitted by our members
Games in WIP (556)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  is "switch (instanceof)" possible?  (Read 8751 times)
0 Members and 1 Guest are viewing this topic.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Posted 2011-02-01 19:17:03 »

As the title suggests, is it possible to have a switch statement choose what to do, depending on the type of an object.
This gets sick, if when I have enough different objects:
1  
2  
3  
4  
5  
6  
7  
8  
if (myObject instanceof Gun) {
}
if (differentObject instanceof Cannon) {
}
..
..
..etc.
..etc.


I'm using KryoNet, that's why.

Can I do this with a switch, or anyway more elegantly when having 120 different objects?

Offline Captain Awesome

Junior Member


Medals: 2


Hi


« Reply #1 - Posted 2011-02-01 20:08:35 »

Maybe create a wrapper class that holds a reference to a Class and a reference to a Runnable, instantiate one wrapper for each class you need and add them to a list. Then simply loop through the list, comparing the Class reference to the class of the object
Offline princec

JGO Kernel


Medals: 369
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #2 - Posted 2011-02-01 20:19:04 »

You use the visitor pattern instead to do this trick. (A quick google will give you the details on that).

Cas Smiley

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline pjt33
« Reply #3 - Posted 2011-02-01 21:29:41 »

A 120-case switch is hardly elegant.
Offline kaffiene
« Reply #4 - Posted 2011-02-01 22:14:23 »

to me, if you're considering a 120 item switch statement, there's a flaw in your design.

Can you perhaps map the classes to an Action like so:

HashMap <Integer, Action>

...where the integer is the hash of the object's class and Action is a class you define with a single 'run' or 'do it' method?
Offline kaffiene
« Reply #5 - Posted 2011-02-01 22:36:03 »

Ok, so I was thinking about this so I decided to code it up:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
public class Main {

    public static void main(String[] args) { new Main();  }

    class A{
        String aString = "State for A";
    }
    class B{
        String bString = "State for B";
    }

    interface Action {
        public void go(Object data);
    }


    Main(){
        HashMap<Integer, Action> map = new HashMap<Integer, Action>();
        map.put(A.class.hashCode(),
                new Action(){
                    public void go(Object data) {
                        System.out.println(((A)data).aString);
                    }});
        map.put(B.class.hashCode(),
                new Action(){
                    public void go(Object data) {
                        System.out.println(((B)data).bString);
                    }});

        Object[] objects = new Object[]{ new A(), new B(), new A(), new B()};

        for(Object o : objects){
            map.get(o.getClass().hashCode()).go(o);
        }
    }

}


The idea here is that the objects array represents your stream of incoming objects from Kryonet.

The output for the above is:
State for A
State for B
State for A
State for B

Obviously, you can register however many Class /Action pairs as you like.  Make sense?
Offline princec

JGO Kernel


Medals: 369
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #6 - Posted 2011-02-01 22:43:04 »

Eeergh Sad

Cas Smiley

Online Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #7 - Posted 2011-02-01 22:43:07 »

The problem here is that a 'newbie' (with all due respect) is learning to program and learning to do networking, with a highlevel API (kryonet).

What ever happened to DataInputStream/DataOutputStream? Learn it there, move on once you feel confident you master the basics: that is, making it work. Starting with a highlevel API is a sure way not to learn the underlying concepts of networking.

On that note: once you converted your code to DataInputStream/DataOutputStream, you obviously haven't solved the original problem yet, but I doubt that problem will still be there. You can simply send Strings, use some refactoring to lookup a method, and invoke that (initially with a byte[] as parameter, containing the remaining bytes of the message). No need to do the switching, let java reflection do the binding for you.

Even better: a text-based protocol -- much easier to debug.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline kaffiene
« Reply #8 - Posted 2011-02-01 22:54:39 »

Eeergh :(

Cas :)

Hmmm.... well, that's the kind of critique that really helps :o) 

It does what the OP requires.  What do you dislike about it?
Offline kaffiene
« Reply #9 - Posted 2011-02-01 22:55:32 »

The problem here is that a 'newbie' (with all due respect) is learning to program and learning to do networking, with a highlevel API (kryonet).

What ever happened to DataInputStream/DataOutputStream? Learn it there, move on once you feel confident you master the basics: that is, making it work. Starting with a highlevel API is a sure way not to learn the underlying concepts of networking.

On that note: once you converted your code to DataInputStream/DataOutputStream, you obviously haven't solved the original problem yet, but I doubt that problem will still be there. You can simply send Strings, use some refactoring to lookup a method, and invoke that (initially with a byte[] as parameter, containing the remaining bytes of the message). No need to do the switching, let java reflection do the binding for you.

Even better: a text-based protocol -- much easier to debug.

You might be right.  I'm still interested in answers to the original question thou
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline princec

JGO Kernel


Medals: 369
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #10 - Posted 2011-02-01 23:07:14 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
interface Processed {
   void process(Processor p);
}

interface Processor {
   void processGun(Gun g);
   void processCannon(Cannon c);
}

class Gun implements Processed {
   void process(Processor p) {
      p.processGun(this);
   }
   void fire() {
   }
}

class Cannon implements Processed {
   void process(Processor p) {
      p.processCannon(this);
   }
   void lightFuse() {
   }
}

...

class Twiddler implements Processor {
    void processGun(Gun g) {
       g.fire();
    }
   
    void processCannon(Cannon c) {
       c.lightFuse();
    }
}

...

Twiddler twiddler = new Twiddler();
for (Processed p : bunchOfThings) {
   p.process(twiddler);
}


Cas Smiley

Offline kaffiene
« Reply #11 - Posted 2011-02-02 01:12:46 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
interface Processed {
   void process(Processor p);
}

interface Processor {
   void processGun(Gun g);
   void processCannon(Cannon c);
}

class Gun implements Processed {
   void process(Processor p) {
      p.processGun(this);
   }
   void fire() {
   }
}

class Cannon implements Processed {
   void process(Processor p) {
      p.processCannon(this);
   }
   void lightFuse() {
   }
}

...

class Twiddler implements Processor {
    void processGun(Gun g) {
       g.fire();
    }
   
    void processCannon(Cannon c) {
       c.lightFuse();
    }
}

...

Twiddler twiddler = new Twiddler();
for (Processed p : bunchOfThings) {
   p.process(twiddler);
}


Cas Smiley

Yup.  Not too bad. 

My solution has the advantage that it doesn't require you to be able modify all the passed objects (yours wouldn't work  if you were passed an object that wasn't a Processed) ... but in this case I don't suppose that matters because Kryonet requires you to code up both halves of the client server arrangement, if I recall correctly.



Offline JL235

JGO Coder


Medals: 10



« Reply #12 - Posted 2011-02-02 03:20:02 »

In improvement on Kaffeines, use generics!
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
interface Handler<T> {
    public void call( T t );
}

class ClassActions {
    private Map<Class<?>, Handler<?>> actions = new HashMap<Class<?>, Handler<?>>();
   
    public <T> void add( Class<T> klass, Handler<T> action )
    {
        actions.put( klass, action );
    }
   
    public <T> void invoke( T obj )
    {
        Class<T> objKlass = obj.getClass();
        Handler<T> handler = (Handler<T>) actions.get( objKlass );
        handler.call( obj );
    }
}

// setup action handlers
actions.add( Gun.class, new Handler<Gun>() {
        public void call( Gun gun ) {
            // gun handler
       }
} );
actions.add( Bullet.class, new Handler<Bullet>() {
        public void call( Bullet bullet ) {
            // Bullet handler
       }
} );
actions.add( Other.class, new Handler<Other>() {
        public void call( Other other ) {
            // some other handler
       }
} );

// run handlers on an object
actions.invoke( bullet );

// run handlers on all objects stored
for ( Object o : myStuff ) {
    actions.invoke( o );
}

The above is untested, but something along those lines should work. But if you could access generic parameters at runtime it could be a bit more terse.

Offline nsigma
« Reply #13 - Posted 2011-02-02 11:00:13 »

In improvement on Kaffeines, use generics!
There was an article with an interesting extension to this on java.net last year that also allows for subclasses http://today.java.net/article/2010/04/26/match-maker-design-pattern-new-place-actions

IMO, all the solutions (including the visitor pattern) are pretty "Eeergh"!  They're all hacks around the fact that Java doesn't do double dispatch.  I've used all these approaches depending on the circumstances.  The visitor pattern is probably the better performing (as mentioned, if you're coding both sides of the equation), but it does involve adding warts to your API!  Smiley

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #14 - Posted 2011-02-02 21:58:19 »

Ok, so I was thinking about this so I decided to code it up:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
public class Main {

    public static void main(String[] args) { new Main();  }

    class A{
        String aString = "State for A";
    }
    class B{
        String bString = "State for B";
    }

    interface Action {
        public void go(Object data);
    }


    Main(){
        HashMap<Integer, Action> map = new HashMap<Integer, Action>();
        map.put(A.class.hashCode(),
                new Action(){
                    public void go(Object data) {
                        System.out.println(((A)data).aString);
                    }});
        map.put(B.class.hashCode(),
                new Action(){
                    public void go(Object data) {
                        System.out.println(((B)data).bString);
                    }});

        Object[] objects = new Object[]{ new A(), new B(), new A(), new B()};

        for(Object o : objects){
            map.get(o.getClass().hashCode()).go(o);
        }
    }

}


The idea here is that the objects array represents your stream of incoming objects from Kryonet.

The output for the above is:
State for A
State for B
State for A
State for B

Obviously, you can register however many Class /Action pairs as you like.  Make sense?

It may be better to use the class as the key rather than assuming the hashcode is unique (even though it should be). You could also use an IdentityHashMap. The disadvantage to using a map is that you lose polymorphism. Ie, you can only look up the concrete type. A mechanism to look up using super classes and interfaces starts to be a lot more complexity than it is worth.

KryoNet just provides you the object, because this is the most flexible. You can use instanceof (sufficient for most cases) or you can implement your own handling. Doing instanceof is very cheap. How many instanceofs are equivalent to one HashMap lookup? Either way, it isn't likely to be a performance bottleneck. Still, you can reduce the number of ifs by ordering them to check the most likely first, grouping instanceof checks by first doing an instanceof on a super class, or implementing your own lookup. Eg, you could make a general purpose listener that uses HashMap as described above (note the classes in the follow code does not exist, you would need to write them):

1  
2  
3  
4  
5  
6  
7  
8  
DispatchListener dispatch = new DispatchListener();
conn.addListener(dispatch);
dispatch.addListener(A.class, new ObjectListener<A>() {
    public void received (Connection conn, A a) {
        ...
    }
});
...


I personally don't find this cleaner than many instanceof checks, but it could perform slightly better if you have many types.

If the sole reason to avoid the many "if instanceof" checks is that you don't like the syntax, you can use Listener.ReflectionListener, eg:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
conn.addListener(new ReflectionListener() {
    public void received (Connection conn, A a) {
        ...
    }
    public void received (Connection conn, B b) {
        ...
    }
    public void received (Connection conn, C c) {
        ...
    }
});


This has the same drawback as using a HashMap (only looks up the concrete type) and there is a negligible performance hit (it uses cached reflection). To get around the former, you can override "public void received (Connection connection, Object object)" and do any instanceof checks you need, calling the super method for anything you don't handle, which lets you use the individual methods to handle the rest.

Offline JL235

JGO Coder


Medals: 10



« Reply #15 - Posted 2011-02-02 22:31:15 »

1  
2  
3  
4  
5  
6  
7  
8  
DispatchListener dispatch = new DispatchListener();
conn.addListener(dispatch);
dispatch.addListener(A.class, new ObjectListener<A>() {
    public void received (Connection conn, A a) {
        ...
    }
});
...


I personally don't find this cleaner than many instanceof checks, but it could perform slightly better if you have many types.
I've not used KyroNet so this relates to the issue in general. I think it would be better to use the HashMap approach if the different handlers relate to entirely separate issues.

Using a big list of instanceof's means you have the different checks coded together in one place. With the HashMap you can have it passed through different sections who each add their own handlers there. This allows you to have a class's handler closer to the class itself (or even within it).

Offline pjt33
« Reply #16 - Posted 2011-02-02 22:47:52 »

I think the best approach depends on the game structure and logic, but one approach which I can see working is a decorator-style approach (I'm not sure whether it's GoF decorator) along the following lines:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
public enum Action
{
    Draw,
    Paint,
    etc;
}

public abstract class GameObject
{
    private final Map<Action, Runnable> actions;

    protected abstract void registerActions();

    protected final void registerAction(Action action, Runnable command) {
        actions.put(action, command);
    }

    public final Runnable getAction(Action action) {
        if (actions == null) {
            actions = new HashMap<Action, Runnable>();
            registerActions();
        }

        return actions.get(action);
    }
}


Subclasses can then call super.registerActions() in their implementations of it, use getAction in registerActions to get the superclass' command and wrap it in another, which they then register, completely override the superclass' command for an action, ....

The main problem I see with this approach is that it's very theoretical - it's nicely OO (albeit more Smalltalk-inspired than pure Java), but it involves a lot of anonymous inner classes and dispatch overhead, and sometimes OO is bad because it makes it hard to follow instruction flow.
Offline princec

JGO Kernel


Medals: 369
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #17 - Posted 2011-02-02 23:51:11 »

...whereas my way is ultra-simple, and as fast as it is possible to get Smiley Which might be significant if you're doing say 5000 of them per frame. Or whatever.

Cas Smiley

Offline JL235

JGO Coder


Medals: 10



« Reply #18 - Posted 2011-02-03 00:14:08 »

...whereas my way is ultra-simple, and as fast as it is possible to get Smiley Which might be significant if you're doing say 5000 of them per frame. Or whatever.
Yours requires adding an interface to the class of every object being handled. You might not always be able to do this and makes the code more bloated then the other solutions.

Not everything is about performance.

Offline loom_weaver

JGO Coder


Medals: 17



« Reply #19 - Posted 2011-02-03 01:41:47 »

While normally this question screams polymorphism as the answer, if there are 120 different classes then I would say the design is suspect.

Time to take off your OO hat and put on your data driven hat.

I would investigate a way to roll all the commonality into a base class which would store a single int or enumeration.  Then instead of switching your code should be able to do what it needs to do via array indexing off that int.
Offline pjt33
« Reply #20 - Posted 2011-02-03 08:25:08 »

...whereas my way is ultra-simple, and as fast as it is possible to get Smiley Which might be significant if you're doing say 5000 of them per frame. Or whatever.
Anything which involves an interface with 120 methods doesn't count as ultra-simple in my book. Not to say that a massive visitor like that is never the best solution - compilers string to mind as an example.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #21 - Posted 2011-02-03 09:31:07 »

What about having an interface with an integer, representing the packet upcode, and then choosing the way to handle it with a switch, depending on the upcode?
I doubt I'll actually need 120 upcodes Smiley

Offline loom_weaver

JGO Coder


Medals: 17



« Reply #22 - Posted 2011-02-03 17:32:43 »

What about having an interface with an integer, representing the packet upcode, and then choosing the way to handle it with a switch, depending on the upcode?
I doubt I'll actually need 120 upcodes Smiley

Seems reasonable.  However if all your classes need to provide an upcode then a method in the base class is probably cleaner than an interface.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #23 - Posted 2011-02-03 18:04:20 »

What about having an interface with an integer, representing the packet upcode, and then choosing the way to handle it with a switch, depending on the upcode?
I doubt I'll actually need 120 upcodes Smiley

Seems reasonable.  However if all your classes need to provide an upcode then a method in the base class is probably cleaner than an interface.

Why would it be cleaner than an interface? Shocked
The only reason I see, is to provide a default upcode, and method. Smiley

Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #24 - Posted 2011-02-03 18:14:56 »

Forget about this craziness and just send some custom text or JSON. Like Riven says, I'd keep it simple at first.

See my work:
OTC Software
Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #25 - Posted 2011-02-03 21:30:34 »

I would investigate a way to roll all the commonality into a base class which would store a single int or enumeration.  Then instead of switching your code should be able to do what it needs to do via array indexing off that int.

An enum could be used, but you would be sending an extra int over the network for no real reason. Note this is an extra overhead of 1 or 2 bytes, so not a big deal. However, this is easily avoided with the solutions in my post above.

Forget about this craziness and just send some custom text or JSON. Like Riven says, I'd keep it simple at first.

The "problem" remains. Instead of 120 'if (object instanceof Xxx)' you would have 120 'if (string.equals("Xxx"))'.

Anyway, I doubt Mads actually has 120 objects and is just annoyed at how the code looks with a bunch of instanceofs. There is no bottleneck.

Online Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #26 - Posted 2011-02-03 22:43:17 »

Forget about this craziness and just send some custom text or JSON. Like Riven says, I'd keep it simple at first.

The "problem" remains. Instead of 120 'if (object instanceof Xxx)' you would have 120 'if (string.equals("Xxx"))'.

I bet you missed how I talked about reflection and methods.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #27 - Posted 2011-02-03 23:24:08 »

I bet you missed how I talked about reflection and methods.

While I agree learning about how networking works is a good idea, I don't think sending strings and doing reflection simpler. "if instanceof" is already about as simple as it gets.

Offline kaffiene
« Reply #28 - Posted 2011-02-03 23:49:41 »

...whereas my way is ultra-simple, and as fast as it is possible to get Smiley Which might be significant if you're doing say 5000 of them per frame. Or whatever.

Cas Smiley

The cost of 5000 method calls or 5000 hash lookups per frame is highly unlikely to be an issue.  Maybe if they were JNI calls, it'd matter.
Offline princec

JGO Kernel


Medals: 369
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #29 - Posted 2011-02-04 00:01:27 »

True, though of course if you were, say, trying to get it running on Android, you'd be seriously thinking about 5000 hashmap lookups being very slow. There's method to my madness. Turns out not everything is a 2.6Ghz i7 these days...!

Cas Smiley

Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Nickropheliac (16 views)
2014-08-31 22:59:12

TehJavaDev (24 views)
2014-08-28 18:26:30

CopyableCougar4 (33 views)
2014-08-22 19:31:30

atombrot (42 views)
2014-08-19 09:29:53

Tekkerue (41 views)
2014-08-16 06:45:27

Tekkerue (35 views)
2014-08-16 06:22:17

Tekkerue (26 views)
2014-08-16 06:20:21

Tekkerue (37 views)
2014-08-16 06:12:11

Rayexar (73 views)
2014-08-11 02:49:23

BurntPizza (49 views)
2014-08-09 21:09:32
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!