2015-09-16 6 views
2

В моей программе пользователю необходимо ввести типы игроков, которые будут иметься в игре. Игроки "human", "good" (для хорошего AI), "bad" (для плохой AI) и "random" (для случайного AI). Каждый из этих игроков имеет свой собственный класс, который расширяет один абстрактный класс под названием PlayerType.Использовать командную строку для создания новых объектов

Моя борьба отображения String на объект, так что я могу А) создать новый объект, используя строку в качестве своего рода ключом и B) получить связанную String от объекта подкласса

В конечном счете, я просто хочу, чтобы неявный String отображался только один раз в коде, поэтому я могу изменить его позже, если потребуется, без рефакторинга.

Я пробовал использовать только простой HashMap, но это кажется неуклюжим с поиском ключей через значения. Кроме того, я предполагаю, что мне придется использовать метод getInstance()Class, который немного менее неуклюжим, и это нормально, если это единственный способ.

+0

Зарегистрируйте свой Classe с фабрикой, использующей строковый ключ классов в качестве ключа, а сам класс - как значение. –

+0

Это постоянный ввод или только когда он запущен? – ChiefTwoPencils

+0

, вероятно, какой-то оператор switch, основанный на доступных именах, а затем просто возвращает новый экземпляр любых совпадений. – CasualT

ответ

4

Что бы я сделал, это создать enum, который по существу функционирует как фабрика для данного типа.

public enum PlayerTypes { 
    GOOD { 
     @Override 
     protected PlayerType newPlayer() { 
      return new GoodPlayer(); 
     } 
    }, 
    BAD { 
     @Override 
     protected PlayerType newPlayer() { 
      return new BadPlayer(); 
     } 
    }, 
    RANDOM { 
     @Override 
     protected PlayerType newPlayer() { 
      return new RandomPlayer(); 
     } 
    }; 

    protected abstract PlayerType newPlayer(); 

    public static PlayerType create(String input) { 
     for(PlayerTypes player : PlayerTypes.values()) { 
      if(player.name().equalsIgnoreCase(input)) { 
       return player.newPlayer(); 
      } 
     } 
     throw new IllegalArgumentException("Invalid player type [" + input + "]"); 
    } 
) 

Потому что тогда вы можете просто назвать его так:

String input = getInput(); 
PlayerTypes.create(input); 

Конечно, вы получите IllegalArgumentException, который вы, вероятно, следует обрабатывать, пытаясь получить вход снова.

EDIT: Видимо, в данном случае, вы можете заменить эту петлю только лишь

return PlayerTypes.valueOf(input).newPlayer(); 

И это будет делать то же самое. Я склонен соответствовать дополнительным параметрам конструктора в enum, поэтому я не думал использовать valueOf(), но он определенно чище.

EDIT2: Единственный способ вернуть эту информацию - определить абстрактный метод в классе PlayerType, который возвращает перечисление PlayerTypes для данного типа.

public class PlayerType { 
    public abstract PlayerTypes getType(); 
} 

public class GoodPlayer extends PlayerType { 
    @Override 
    public PlayerTypes getType() { 
     return PlayerTypes.GOOD; 
    } 
} 
+1

Если вы готовы разбираться в верхнем/нижнем корпусе, вы просто возвращаете PlayerTypes.valueOf (ввод) .newPlayer(); '. Вы получите исключение IllegalArgumentException бесплатно, поскольку это исключение, которое 'valueOf()' выдает, если оно не может соответствовать типу. – azurefrog

+0

@azurefrog действительный пункт, я отредактировал его сейчас, когда я не на пристойной телефонной клавиатуре, спасибо за информацию – EpicPandaForce

+0

Это отвечает моей проблеме на A). Возможно ли иметь экземпляр, скажем, «GoodPlayer», а затем захватить имя, к которому он привязан? (для моей проблемы B) Я предполагаю, что перечисление должно быть двунаправленным – Kyefer

0

Два основных варианта я могу думать:

Использование Class.newInstance(), как вы упомянули (не уверен, что если у вас именно этот путь в виду):

// Set up your map 
Map<String, Class> classes = new HashMap<String, Class>(); 
classes.put("int", Integer.class); 
classes.put("string", String.class); 

// Get your data 
Object s = classes.get("string").newInstance(); 

Вы можете использовать Class.getDeclaredConstructor . newInstance если вы хотите использовать конструктор с аргументами (example).


Другой вариант заключается в использовании switch:

Object getObject(String identifier) { 
    switch (identifier) { 
     case "string": return new String(); 
     case "int": return new Integer(4); 
    } 
    return null; // or throw an exception or return a default object 
} 
+0

«Новый» (от Java 8), как предлагается в [другом ответе] (http://stackoverflow.com/a/32614878/1711796), возможно, лучше моего первого варианта. – Dukeling

1

Мне нравится ответ предоставленный эпосе, но я не нахожу карты, чтобы быть неуклюжим. Таким образом, можно сохранить карту и получить вызов конструктора напрямую.

Map<String, Supplier<PlayerType> map = new HashMap<>(); 
map.put("human", Human::new); 
Human h = map.get("human").get(); 
+0

Можно ли сделать это с помощью нестандартных конструкторов? – Kyefer

+0

Нет, вам нужно будет использовать немного другой интерфейс, который позволяет использовать параметры в зависимости от того, сколько у вас есть. Или предложите метод init для вызова экземпляра. @Kyefer – ChiefTwoPencils

0

Одним из возможных решений:

public class ForFunFactory { 

    private ForFunFactory() { 
    } 

    public static AThing getTheAppropriateThing(final String thingIdentifier) { 
     switch (thingIdentifier) { 
      case ThingImplApple.id: 
       return new ThingImplApple(); 
      case ThingImplBanana.id: 
       return new ThingImplBanana(); 
      default: 
       throw new RuntimeException("AThing with identifier " 
         + thingIdentifier + " not found."); 
     } 
    } 
} 

public interface AThing { 
    void doStuff(); 
} 

class ThingImplApple implements AThing { 

    static final String id = "Apple"; 

    @Override 
    public void doStuff() { 
     System.out.println("I'm an Apple."); 
    } 

} 

class ThingImplBanana implements AThing { 

    static final String id = "Banana"; 

    @Override 
    public void doStuff() { 
     System.out.println("I'm a Banana."); 
    } 

}