Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (804)
Games in Android Showcase (237)
games submitted by our members
Games in WIP (867)
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  
  getting rid of "instanceof"  (Read 18135 times)
0 Members and 1 Guest are viewing this topic.
Offline nsigma
« Reply #30 - Posted 2017-01-13 12:16:54 »

@cylab - I use that "pattern" a lot, but then it's basically the same as the Lookup mechanism in the NetBeans platform.  Not sure if it's the fastest approach, but it's definitely one of the nicer ones.  Smiley

Incidentally, getHandler() could also return Optional to not require the supports() method, or you could even pass a consumer into a utility method on entity that does that for you. Hey, I like keeping the logic code succinct and less error prone.  Grin 

1  
2  
3  
entities.forEach(e -> e.getHandler(PoisonGasEffect.class).ifPresent(h -> h.onPoison(1)));

entities.forEach(e ->  e.with(PoisonGasEffect.class, h -> h.onPoison(1)));

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline cylab

JGO Kernel


Medals: 195



« Reply #31 - Posted 2017-01-13 12:59:36 »

@cylab - I use that "pattern" a lot, but then it's basically the same as the Lookup mechanism in the NetBeans platform.  Not sure if it's the fastest approach, but it's definitely one of the nicer ones.  Smiley
Yupp. Can't deny my Netbeans RCP past...


1  
entities.forEach(e ->  e.with(PoisonGasEffect.class, h -> h.onPoison(1)));

For the sake of completeness, this is, how Entity would look like, then:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
abstract class Entity {
    private Map<Class<?>, Object> handlers = new HashMap<>();

    protected <T> void register(Class<T> type, T instance) {
        handlers.put(type, instance);
    }

    public <T> boolean supports(Class<T> type) {
        return handlers.containsKey(type);
    }

    // support java 8 style optional lambda execution
    public <T> void with(Class<T> type, Consumer<T> consumer) {
        T handler = type.cast(handlers.get(type))
        if(handler != null)
            consumer.accept(handler);
    }
}

Mathias - I Know What [you] Did Last Summer!
Offline DayTripperID

Senior Devvie


Medals: 8
Projects: 1
Exp: 1-3 months


Living is good!


« Reply #32 - Posted 2017-01-14 21:49:01 »

Hey guys,

I just checked this post for the first time in a couple days, and I see there is a lot more to read. =) I did a lot more refactoring. Actually I have  been rebuilding the game from the ground up, which has turned out to be the best thing I could have done. One of the things I did in thee process is move the Visitor implementation out of the Mob class and into the MobState class:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
package breakit.mob.state;

import breakit.mob.Mob;
import breakit.visitor.Visitable;
import breakit.visitor.Visitor;
import engine.graphics.Screen;

public interface MobState extends Visitable, Visitor {
   
   public MobState init(Mob m);
   
   public MobState update();
   
   public void render(Screen screen);
   
   public void processCollision(Mob m);
   
   public Mob getMob();
   
   public void setMob(Mob m);
   
   public Visitor getVisitor();
}



This solved the "instanceof" dilemma immediately. Now when I process a collision it is more like this:

1  
2  
3  
4  
5  
6  
public void visit(BallStateNormal visitable) {
      Ball ball = (Ball) visitable.getMob();
      brick.bounceBall(ball);
      ball.addMultiplier(1);
      multiplier = ball.getMultiplier();
   }


Here is the state class that contains that visitor method for total context:
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  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
package breakit.mob.brick;

import breakit.gamestate.World;
import breakit.mob.Mob;
import breakit.mob.ball.Ball;
import breakit.mob.ball.BallStateNormal;
import breakit.mob.state.AbstractMobState;
import breakit.mob.state.MobState;
import breakit.visitor.Visitor;
import engine.graphics.Screen;

public class BrickStateDestructible extends AbstractMobState {

   private Brick brick = null;
   private int multiplier = 0;
   
   @Override
   public MobState init(Mob m) {
      brick = (Brick) m;
      brick.setCol(0xffff00);
      return this;
   }
   
   @Override
   public MobState update() {
      if(brick.getLife() == 0) {
         Integer points = multiplier * 100;
         World w = (World) brick.getGameState();
         w.addParticleSpawner((int)brick.getx(), (int)brick.gety(), 100);
         w.addFloatingText((int)brick.getx(), (int)brick.gety() + 1, points.toString());
         brick.remove();
      }
      return this;
   }

   @Override
   public void render(Screen screen) {
      screen.drawRect((int)brick.getx(), (int)brick.gety(), brick.getWidth(), brick.getHeight(), brick.getCol());
     
   }

   @Override
   public void processCollision(Mob m) {
      accept(m.getMobState().getVisitor());
     
   }

   @Override
   public Mob getMob() {
      return brick;
   }

   @Override
   public void setMob(Mob m) {
      brick = (Brick) m;
   }
   
   public void visit(BallStateNormal visitable) { //this method here
      Ball ball = (Ball) visitable.getMob();
      brick.bounceBall(ball);
      ball.addMultiplier(1);
      multiplier = ball.getMultiplier();
   }

   @Override
   public void accept(Visitor visitor) {
      visitor.visit(this);
   }
}


So instead of each visit needing to check which state the Mob is in and respond accordingly, it now knows exactly what state it is dealing with and can just act.  

Right now I have Mob classes which represent an game entity. The Mob references a separate state class that can be discarded and replaced by a newly created state on the fly. Brick is a bad example because it pretty much holds the same state for its lifetime. A brick is destructible or it is indestructible. It never changes state, although technically it could.

Ball, on the other hand, can change states between Normal, Power, and Giant on the fly. So really, the Mob just becomes a container for the fields such as x, y, xa, ya, color, etc. All game logic and rendering is done in the state classes, including the collision visits. Each state handles the game logic and collision uniquely:

package breakit.mob.ball;

import breakit.config.Config;
import breakit.mob.Mob;
import engine.graphics.Screen;
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  
public class Ball extends Mob {

   private int multiplier = 0;
   
   public Ball(int x, int y) {
      super(x, y);
      setWidth(4);
      setHeight(4);
      setx(Config.WINDOW_WIDTH / 2);
      sety(Config.WINDOW_HEIGHT / 2);
      setxdir(Mob.LEFT);
      setydir(Mob.UP);
      setxspeed(2);
      setyspeed(2);
   }
   
     @Override
   public void update() {
      setMobState(getMobState().update());  
   }

      @Override
   public void render(Screen screen) {
      getMobState().render(screen);
     
   }

   public void addMultiplier(int n) {
      multiplier += n;
   }
   
   public Integer getMultiplier() {
      return multiplier;
   }
   
   public void setMultiplier(int n) {
      multiplier = n;
   }
}


I'm starting to see why composition is emphasized so heavily over inheritance, but mastering the art of decoupling is definitely a challenge. BTW I still have to read all the posts since I've been away, I just wanted to post an update of my progress. Sorry for the wall of code!

Living is good!
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline DayTripperID

Senior Devvie


Medals: 8
Projects: 1
Exp: 1-3 months


Living is good!


« Reply #33 - Posted 2017-01-14 22:32:42 »

@cylab - Your post (reply # 29) showed me that you can use a class type as a key in a HashMap, so thank you for that.  Pointing I wanted to do that one point and didn't know it was actually possible. Also, I like the implementation you suggest as it is very clean. I'll probably try that at some point, but for now this implementation is working well.

I read in several spots on the thread that casting might smell of bad design, which I do in places, namely where a state is assigned to a Mob:
1  
2  
3  
4  
5  
6  
@Override
   public MobState init(Mob m) {
      brick = (Brick) m;
      brick.setCol(0xffff00);
      return this;
   }


The problem is that I can't assign the type and recognize the subclass methods without casting. It doesn't generate a compile time error if you pass the wrong type as an argument, but I have tested it and it will throw a ClassCastException and print a stack trace. So I was wondering if this is considered bad design and if there is a better approach? Thank you all for contributing to the thread- your expert advice has been very enlightening!

Living is good!
Offline Abuse

JGO Ninja


Medals: 71


falling into the abyss of reality


« Reply #34 - Posted 2017-01-14 23:31:18 »

@cylab - Your post (reply # 29) showed me that you can use a class type as a key in a HashMap, so thank you for that.  Pointing I wanted to do that one point and didn't know it was actually possible. Also, I like the implementation you suggest as it is very clean. I'll probably try that at some point, but for now this implementation is working well.

I read in several spots on the thread that casting might smell of bad design, which I do in places, namely where a state is assigned to a Mob:
1  
2  
3  
4  
5  
6  
@Override
   public MobState init(Mob m) {
      brick = (Brick) m;
      brick.setCol(0xffff00);
      return this;
   }


The problem is that I can't assign the type and recognize the subclass methods without casting. It doesn't generate a compile time error if you pass the wrong type as an argument, but I have tested it and it will throw a ClassCastException and print a stack trace. So I was wondering if this is considered bad design and if there is a better approach? Thank you all for contributing to the thread- your expert advice has been very enlightening!

In that example, it looks like your MobState super-class should be a generic type, with the init method using the class' parameterized type for its parameter.
Something like:

1  
2  
3  
4  
abstract class MobState<T> {

public abstract MobState init(T m);
}


The return type could probably be parameterized too, though I've no idea what your MobState / Mob class heirarchy looks like.
Offline nsigma
« Reply #35 - Posted 2017-01-15 12:12:00 »

This solved the "instanceof" dilemma immediately. Now when I process a collision it is more like this:

1  
2  
3  
4  
5  
public void visit(BallStateNormal visitable) {
      Ball ball = (Ball) visitable.getMob();
      brick.bounceBall(ball);
                //
}


hmm .. removing the instanceof and having a blind cast is not improving things!  Doing instanceof -> cast, using visitors, pattern matching or heterogenous containers are all about filtering a collection of entities at runtime for those that are a / have a particular capability.  If you have a blind cast like that you either know at compile time the type returned, in which case that's what the method should return, or you're making an assumption that is overriding compile time checks and will eventually blow up on you at runtime.

@Abuse may be right that generics are the way to go here.  Although your concrete subclasses probably don't want to be generic or you're likely to end up needing to know genetic types at runtime (a PITA!).  So, something along the lines perhaps of BallStateNormal extends AbstractMobState<Ball>

However, also don't forget that overridden methods in Java can return a more specific subtype, so visitable.getMob() on BallStateNormal could return Ball already without generics.

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline delt0r

JGO Wizard


Medals: 145
Exp: 18 years


Computers can do that?


« Reply #36 - Posted 2017-01-15 20:23:39 »

And all this discussion, interesting discussion at that, could be solved with double dispatch.

The visitor pattern, indeed quite a few of the GoF patterns solve problems that often functional languages solve directly.  Having said that double dispatch is not in many. Lisp and in particular CLOS in lisp and its derivatives.

In fact the Entity system i use does sort of do Generic methods like CLOS. It works pretty good.

I have no special talents. I am only passionately curious.--Albert Einstein
Offline DayTripperID

Senior Devvie


Medals: 8
Projects: 1
Exp: 1-3 months


Living is good!


« Reply #37 - Posted 2017-01-15 22:15:38 »

OK, so I implemented generic typing on MobState and it worked swimmingly:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
public interface MobState<T extends Mob> extends Visitable, Visitor {
   
   public MobState<T> init(T t);
   
   public MobState<T> update();
   
   public void render(Screen screen);
   
   public void processCollision(Mob m);
   
   public T getMob();
   
   public void setMob(T t);
   
   public Visitor getVisitor();
}


So now Mob looks like this (Minus a bunch of fields and methods irrelevant to the thread, to save space):

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
public abstract class Mob {
   
   private MobState<? extends Mob> ms = null;
   
   private double x = 0;
   private double y = 0;
   
   public Mob(int x, int y) {
      this.x = x;
      this.y = y;
   }
   
   public final MobState<? extends Mob> getMobState() {
      return ms;
   }
   

   public final void setMobState(MobState<? extends Mob> s) {
      ms = s;
   }
}


And an example of a concrete MobState object looks like this:

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  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
public class BrickStateSolid extends AbstractMobState<Brick> {
   
   private Brick brick = null;
   
   @Override
   public MobState<Brick> init(Brick b) {
      brick = b;
      brick.setCol(0x808080);
      return this;
   }
   
   @Override
   public MobState<Brick> update() {
      return this;
   }

   @Override
   public void render(Screen screen) {
      int c = brick.getCol();
      int x = (int)brick.getx();
      int y = (int)brick.gety();
      int w = brick.getWidth();
      int h = brick.getHeight();
     
      for(int i = 0; i < brick.getHeight() / 2; i++) {
         screen.drawRect(x++, y++, w, h, c);
         w -= 2;
         h -= 2;
         c -= 0x202020;  
      }
   }

   @Override
   public void processCollision(Mob m) {
      accept(m.getMobState().getVisitor());
   }

   @Override
   public Brick getMob() {
      return brick;
   }

   @Override
   public void setMob(Brick b) {
      brick = b;
   }
   
   @Override
   public void visit(BallStateNormal visitable) {
      Ball ball = visitable.getMob();
      brick.bounceBall(ball);
   }

   @Override
   public void accept(Visitor visitor) {
   }
}


Advantage: no type casting and no "instanceof", meaning bad type-checks are caught at compile time.
So something like MobState<Ball> mobState = new PlayerStateAlive().init(player); won't compile, and once the type is set, there is no way to pass a bad object to a visitor object.

Disadvantage: kind of hard to read if you aren't used to it. Specifically, I'm using <? extends Mob> in the Mob class, which is apparently the right one, because <? super Mob> generates this error at the return statement of MobState<? super Mob> getMobState(MobState<?super Mob>):
1  
Type mismatch: cannot convert from MobState<capture#1-of ? extends Mob> to MobState<? super Mob>


However, <? super Mob> generates NO error in setMobState(MobState<? super Mob> ms)

<? extends Mob> was my first guess, but I tried <? super Mob> for the hell of it, and I really have no idea what the difference between the two is, other than I used <? super Object> in a data structures class that used it in a binary node interface:

1  
public interface BinaryNodeInterface<T extends Comparable<? super T>>


Whew! Java sure is verbose. Anyway, I was wondering if anybody had a good explanation of the difference between <? super >and <? extends>. I googled around and found some explanations, but it didn't really sink in for me.

EDIT: as it turns out, the reason I was getting an error in MobState<? super Mob> getMobState() was because I hadn't set the type of the ms field to MobState<? super Mob> and it was still MobState<? extends Mob>. Once that was corrected, <? super Mob> still generated compiler errors wherever I actually tried to set the MobState:

1  
2  
MobState<Brick> mobState = new BrickStateDestructible().init(brick);
      brick.setMobState(mobState);


The above statement would generate the error:

1  
The method setMobState(MobState<? super Mob>) in the type Mob is not applicable for the arguments (MobState<Brick>)


Simple enough: keep it <? extends Mob>. My best understanding is that with <? super Mob> brick.setMobState(mobState) would only accept  of MobState<Mob> and not MobState<Player>, MobState<Ball>, etc. Since Mob is not specific enough, I don't think that would be very useful, as it would defeat the purpose of what I'm trying to accomplish in the first place.

Living is good!
Offline philfrei
« Reply #38 - Posted 2017-01-16 04:42:11 »

Quote
Whew! Java sure is verbose. Anyway, I was wondering if anybody had a good explanation of the difference between <? super >and <? extends>. I googled around and found some explanations, but it didn't really sink in for me.

They are kind of like a greater than or less than, but referring to the extends chain. One gets the class and all super classes, the other gets the class and all sub classes.

music and music apps: http://adonax.com
Offline princec

« JGO Spiffy Duke »


Medals: 1136
Projects: 3
Exp: 20 years


Eh? Who? What? ... Me?


« Reply #39 - Posted 2017-01-16 09:33:34 »

Usually I end up trying every combination of super and extends until it compiles. The logic behind it is indeed logic, but so hard to grasp just by glancing at something that even now after, what, over 10 years of using generics, I still can't get it right first time.

Quote
And all this discussion, interesting discussion at that, could be solved with double dispatch.
Heh Smiley And another language. But here we are.

Cas Smiley

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline DayTripperID

Senior Devvie


Medals: 8
Projects: 1
Exp: 1-3 months


Living is good!


« Reply #40 - Posted 2017-01-16 12:57:33 »

@philfrei Thank you. Yes, it dawned on me when I woke up in the middle of the night and was trying to fall back asleep. <? extends T> is any class that, well extends T, lol. <? super T> is any class that is extended by T. Now I have the concept on lockdown.

Living is good!
Offline VoidBuffer

Junior Devvie


Medals: 4
Exp: 2 years



« Reply #41 - Posted 2017-01-16 23:02:01 »

I found this floating about after reading the thread. It had some interesting comparisons: http://stackoverflow.com/a/26514984/7323222

Everyone is obviously at different stages of development, but I'll be using the easier-to-follow instanceof until I personally see bottlenecks.
Pages: 1 [2]
  ignore  |  Print  
 
 

 
Riven (397 views)
2019-09-04 15:33:17

hadezbladez (5280 views)
2018-11-16 13:46:03

hadezbladez (2204 views)
2018-11-16 13:41:33

hadezbladez (5544 views)
2018-11-16 13:35:35

hadezbladez (1150 views)
2018-11-16 13:32:03

EgonOlsen (4585 views)
2018-06-10 19:43:48

EgonOlsen (5462 views)
2018-06-10 19:43:44

EgonOlsen (3119 views)
2018-06-10 19:43:20

DesertCoockie (4016 views)
2018-05-13 18:23:11

nelsongames (4708 views)
2018-04-24 18:15:36
A NON-ideal modular configuration for Eclipse with JavaFX
by philfrei
2019-12-19 19:35:12

Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04: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!