Need help extrapolating Java code

If anyone is familiar with Rebecca Wirfs-Brock, she has a piece of Java code that was found in her book, Object Design: Roles, Responsibilities, and Collaboration.

Here is the quote> Applying double dispatch to a specific problem To implement the game Rock, Paper, Scissors, we need to write code that determines whether one object will "hit" another. The game has nine possible outcomes based on three types of objects. The number of interactions is the cross product of the kinds of objects. Case or switch statements are often governed by the type of data that works. the object-oriented equivalent of a language must base its actions on the class of another object. In Java, it looks like this Here is the Java code snippet on page 16 '

  import java.util.*;     
  import java.lang.*;   

 public class Rock
{
public static void main(String args[])
{

}

public static boolean beats(GameObject object)
{
    if (object.getClass.getName().equals("Rock"))
    {
        result = false;
    }
    else if (object.getClass.getName().equals("Paper"))
    {
        result = false;         
    }
    else if(object.getClass.getName().equals("Scissors"))
    {
            result = true;
    }
    return result;
}

      

} '

===> This is not a good solution. First, the receiver must know too much about this argument. Second, there is one of these nested conditionals in each of the three classes. If new types of objects can be added to the game, each of the three classes must be changed. Can anyone please share with me how to get this "less optimal" piece of code to work in order for it to "work". She continues to show the best way, but I will spare you. thanks

+2


a source to share


5 answers


So here's how I fixed it. First, I created a new GameObject interface as they reference it!

 public interface GameObject
 {
 public boolean beats(GameObject g);
 }

      

The type didn't exist, so referencing it won't work that well.



Here is my new code for Rock, with comments on the changes:

 import java.util.*;
 import java.lang.*;

 public class Rock implements GameObject //Need to be an instance of GameObject somehow!
 {
 public static void main(String args[])
     {

     }

 public boolean beats(GameObject object) //This isn't static anymore
 {
 boolean result = false; //Need to declare and initialize result
 if (((Object)object).getClass().getName().equals("Rock")) //getClass should have ()
     { 
     result = false;
     }
 else if (object.getClass().getName().equals("Paper")) //getClass should have ()
     {
     result = false;
     }
 else if(object.getClass().getName().equals("Scissors")) //getClass should have ()
     {
     result = true;
     }
return result;
 }
 }

      

EDIT: You seemed to be asking for how to fix the code, not the best way to do it. I believe it should be good to go for you now.

-2


a source


I would start by defining the RPSSystem and RPSObject classes. The code for creating a classic RPS game would look like this:

RPSObject rock = new RPSObject("Rock");
RPSObject paper = new RPSObject("Paper");
RPSObject scissors = new RPSObject("Scissors");
RPSSystem classicRPS = new RPSSystem(rock, paper, scissors);
// new RPSSystem(Collection<RPSObject> objects) possible too
classicRPS.defineBeatsRule(rock, scissors);
classicRPS.defineBeatsRule(paper, rock);
classicRPS.defineBeatsRule(scissors, paper);

      

RPSSystem will have a method

int fight(RPSObject a, RPSObject b)

      

which will return -1 when it a

wins, 1 when it b

wins, and 0 when the outcome is undefined. Internally, RPSObjects can be stored in a list, and beat rules can be stored in a matrix (columns and rows will match the indices of the objects in the list). If multiple instances of a similar RPSObject are allowed, the corresponding RPSObject method must be written.

Having a separate class for each object in the system seems too complicated.

EDIT:



Complete classes:

package rpsgame;

public final class RPSObject {
    private final String name;

    public RPSObject(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String toString() {
        return getName();
    }
}

      


package rpsgame;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public final class RPSSystem {

    private final List<RPSObject> objects;
    private final int[][] beatsRules;

    public static final int WINS = 1;
    public static final int TIE = 0;
    public static final int LOSES = -1;


    public RPSSystem(RPSObject... objects) {
        this.objects = Arrays.asList(objects.clone());
        this.beatsRules = new int[objects.length][objects.length];
    }

    void defineBeatsRule(RPSObject winner, RPSObject loser) {
        if (winner.equals(loser)) throw new IllegalArgumentException();

        int winnerIndex = getObjectIndex(winner);
        int loserIndex = getObjectIndex(loser);

        beatsRules[winnerIndex][loserIndex] = WINS;
        beatsRules[loserIndex][winnerIndex] = LOSES;
    }

    public int fight(RPSObject a, RPSObject b) {
        int aIndex = getObjectIndex(a);
        int bIndex = getObjectIndex(b);

        return beatsRules[aIndex][bIndex];
    }

    public List<RPSObject> getObjects() {
        return Collections.unmodifiableList(objects);
    }

    private int getObjectIndex(RPSObject o) {
        int index = objects.indexOf(o);
        if (index < 0) throw new IllegalArgumentException();
        return index;
    }

    // test
    public static void main(String[] args) {

        RPSSystem classicRPS = buildClassicRPS();

        List<RPSObject> objects = classicRPS.getObjects();

        for (RPSObject a: objects) {
            for (RPSObject b: objects) {
                int result = classicRPS.fight(a, b);
                switch (result) {
                    case RPSSystem.WINS:
                        System.out.println(a + " beats " + b);
                        break;
                    case RPSSystem.TIE:
                        System.out.println(a + " vs " + b + " is tied");
                        break;
                    case RPSSystem.LOSES:
                        System.out.println(a + " loses against " + b);
                        break;
                }   
            }
        }
    }

    private static RPSSystem buildClassicRPS() {
        RPSObject rock = new RPSObject("Rock");
        RPSObject paper = new RPSObject("Paper");
        RPSObject scissors = new RPSObject("Scissors");

        RPSSystem classicRPS = new RPSSystem(rock, paper, scissors);

        classicRPS.defineBeatsRule(rock, scissors);
        classicRPS.defineBeatsRule(paper, rock);
        classicRPS.defineBeatsRule(scissors, paper);
        return classicRPS;
    }
}

      

Just add RPSSystem.EVERYONE_DIES

and defineEveryoneDiesRule(...)

and you are ready to

rps.add(atombomb);
rps.defineBeatsRule(atombomb, scissors);
rps.defineBeatsRule(atombomb, rock);
rps.defineBeatsRule(atombomb, paper);
rps.defineEveryoneDiesRule(atombomb, atombomb);

      

+3


a source


Use an enum to work with it ( RPSObj

), which has a method beats(RPSObj o)

, with each element of the enum having an a Set

which is stored as beatset

. Then the method beats(RPSObj o)

can do return beatset.contains(o);

. Symptoms :)

Edit: you can actually use EnumSet as a Set implementation, which should be even more efficient than other implementation implementations. :)

+2


a source


You can watch this thread:

Using inheritance and polymorphism to solve a common game problem

It seems that there is the same question around.

+1


a source


I think personally I would just have a utility-like class that contains a "beats" method. The bits method will take two GameObjects as parameters.

This way I could just pass two objects (rock, paper, or scissors) and do the logic I needed. Now, if you add a new object, you don't change anything except the "beats" method in the utility class, keeping things encapsulated from your main one.

Ryan's link is good, it has a few other ideas to handle this situation.

+1


a source







All Articles