简体   繁体   中英

Java: Avoid 'instanceof' when adding, or removing elements from list

I'm creating a game. A game has a GameMap. In order to track state of things on the GameMap, I wanted to create ArrayLists for each of those things. The problem is, I don't want to have to create separate methods for adding, or removing items from each type of ArrayList. I'm a newb so of course the first thing I can think of is the 'instanceof' operator.

Keep in mind that, at present, GameMap is a discrete class, and not an interface, or abstract class. The intent is to instantiate a GameMap object when the game is initialized.

public class GameMap {

//GameMap has a name
private String mapName;

//GameMap has rooms
private ArrayList<Room> roomsOnMap;

//GameMap has items
private ArrayList<Item> itemsOnMap;

//GameMap has people
private ArrayList<Person> peopleOnMap;

//construct GameMap with rooms, items, and people
private GameMap(String mapName, ArrayList someRooms, ArrayList someItems, ArrayList somePeople)
{
    this.mapName = mapName;
    this.roomsOnMap = someRooms;
    this.itemsOnMap = someItems;
    this.peopleOnMap = somePeople;
}

public void addObject(Object o)
{
    //HOW DO I AVOID THIS?
    if(o instanceof Room)
    {
        roomsOnMap.add((Room) o);
    }
    else if(o instanceof Item)
    {
        itemsOnMap.add((Item) o);
    }
    else if(o instanceof Person)
    {
        peopleOnMap.add((Person) o);
    }
}

Use overloaded methods:

void addObject(Room room) {
  roomsOnMap.add(room);
}

void addObject(Item item) {
  itemsOnMap.add(item);
}

..

Here is one trick:

Map<Class, ArrayList> map = new HashMap<Class, ArrayList>();
map.put(Room.class, roomsOnMap);
map.put(Item.class, itemsOnMap);
map.put(Person.class, peopleOnMap);

// ...

public void addObject(Object o)
{
    map.get(o.getClass()).add(o); // be aware of NullPointerException here
}

Though, I recommend to use overloaded methods for this.

You hunch that instanceOf may not be a very good idea is correct.

If Rooms, Items and Persons are some kind of " GameElements " or " MapElements " you can have a relationship between them via a common parent:

enum ElementType
{
    PERSON, ITEM, ROOM;
}

interface MapElement
{
    public ElementType getType();
}

class Room implements MapElement
{
    public ElementType getType()
    {
        return ElementType.ROOM;
    }
    //other attributes and methods...
}

class Person implements MapElement
{
    public ElementType getType()
    {
        return ElementType.PERSON;
    }
    //other attributes and methods...
}

class Item implements MapElement
{
    public ElementType getType()
    {
        return ElementType.ITEM;
    }
    //other attributes and methods...
}

Then your add method can operate on this interface

public void addObject(MapElement e)
{
    if(e.getType == ElementType.Room)
    {
        roomsOnMap.add((Room) e);
    }
    ...
}

Do this only if the elements are related. If they are not, you should have separate methods. Having a single method to do this may look efficient, but you do not gain anything. To any class using this class (and the developer who writes it), separate add methods (if the elements are not related) is more intuitive than having a generic add with Object as a parameter. You don't lose anything that way, not even a few lines of code, if fact your classes and methods become more straightforward.

If you want to avoid using instanceof try the Visitor design pattern.

Here you can find it's description: http://www.oodesign.com/visitor-pattern.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM