As many may know, I am developing a Trading Card Game for the Coding Challenge.
I have just completed the Field
class and have not used it in practice yet, so all theoretical and looking for a review.
Using Java 8 and all custom classes will be included in this post as well, if you wish you can see all source on GitHub.
The field currently only consists of monsters that can be on the field, though in the future other types of cards may be added.
The Field class:
public class Field {
private final MonsterCard[] monsters;
public Field(final int monsterCapacity) {
Arguments.requirePositive(monsterCapacity, "monster capacity");
this.monsters = new MonsterCard[monsterCapacity];
}
public void setMonster(final int index, final MonsterCard monsterCard) {
Arguments.requireIndexInRange(index, 0, monsters.length);
Objects.requireNonNull(monsterCard);
monsters[index] = monsterCard;
}
public MonsterCard getMonster(final int index) {
Arguments.requireIndexInRange(index, 0, monsters.length);
MonsterCard monsterCard = monsters[index];
States.requireNonNull(monsterCard, NoSuchElementException::new, "no monster exists on index: " + index);
return monsterCard;
}
public boolean hasMonster(final int index) {
Arguments.requireIndexInRange(index, 0, monsters.length);
return (monsters[index] != null);
}
public MonsterCard destroyMonster(final int index) {
Arguments.requireIndexInRange(index, 0, monsters.length);
MonsterCard monsterCard = monsters[index];
States.requireNonNull(monsterCard, NoSuchElementException::new, "no monster exists on index: " + index);
monsters[index] = null;
return monsterCard;
}
public Stream<MonsterCard> getMonsters() {
return Arrays.stream(monsters).filter(obj -> obj != null);
}
public int getMonsterCapacity() {
return monsters.length;
}
@Override
public String toString() {
return "Field(" + Arrays.toString(monsters) + ")";
}
}
The unit tests:
public class FieldTest {
static {
assertTrue(true);
}
@Test
public void testConstructor() {
new Field(1);
new Field(6);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorZeroMonsterCapacity() {
new Field(0);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorNegativeMonsterCapacity() {
new Field(-1);
}
@Test
public void testSetMonster() {
Field field = new Field(6);
MonsterCard monsterCard = createMonsterCard();
field.setMonster(0, monsterCard);
assertEquals(monsterCard, field.getMonster(0));
MonsterCard monsterCard2 = createMonsterCard2();
field.setMonster(5, monsterCard2);
assertEquals(monsterCard2, field.getMonster(5));
}
@Test(expected = IndexOutOfBoundsException.class)
public void testSetMonsterUnderRange() {
Field field = new Field(6);
field.setMonster(-1, createMonsterCard());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testSetMonsterAboveRange() {
Field field = new Field(6);
field.setMonster(6, createMonsterCard());
}
@Test(expected = NullPointerException.class)
public void testSetMonsterMonsterCardNull() {
Field field = new Field(6);
field.setMonster(0, null);
}
@Test
public void testGetMonster() {
Field field = new Field(6);
MonsterCard monsterCard = createMonsterCard();
field.setMonster(0, monsterCard);
assertEquals(monsterCard, field.getMonster(0));
}
@Test(expected = IndexOutOfBoundsException.class)
public void testGetMonsterUnderRange() {
Field field = new Field(6);
field.getMonster(-1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testGetMonsterAboveRange() {
Field field = new Field(6);
field.getMonster(6);
}
@Test(expected = NoSuchElementException.class)
public void testGetMonsterNoMonster() {
Field field = new Field(6);
field.getMonster(0);
}
@Test
public void testHasMonster() {
Field field = new Field(6);
field.setMonster(0, createMonsterCard());
assertTrue(field.hasMonster(0));
}
@Test(expected = IndexOutOfBoundsException.class)
public void testHasMonsterUnderRange() {
Field field = new Field(6);
field.hasMonster(-1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testHasMonsterAboveRange() {
Field field = new Field(6);
field.hasMonster(6);
}
@Test
public void testDestroyMonster() {
Field field = new Field(6);
MonsterCard monsterCard = createMonsterCard();
field.setMonster(0, monsterCard);
assertEquals(monsterCard, field.destroyMonster(0));
assertFalse(field.hasMonster(0));
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDestroyMonsterUnderRange() {
Field field = new Field(6);
field.destroyMonster(-1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDestroyMonsterAboveRange() {
Field field = new Field(6);
field.destroyMonster(6);
}
@Test(expected = NoSuchElementException.class)
public void testDestroyMonsterNoMonster() {
Field field = new Field(6);
field.destroyMonster(0);
}
@Test
public void testGetMonsters() {
Field field = new Field(6);
assertEquals(0, field.getMonsters().count());
assertFalse(field.getMonsters().anyMatch(m -> m == null));
MonsterCard monsterCard = createMonsterCard();
MonsterCard monsterCard2 = createMonsterCard2();
field.setMonster(0, monsterCard);
field.setMonster(1, monsterCard2);
List<MonsterCard> list = field.getMonsters().collect(Collectors.toList());
assertTrue(list.contains(monsterCard));
assertTrue(list.contains(monsterCard2));
}
@Test
public void testGetMonsterCapacity() {
Field field = new Field(6);
assertEquals(6, field.getMonsterCapacity());
}
@Test
public void testToString() {
Field field = new Field(5);
MonsterCard monsterCard = createMonsterCard();
MonsterCard monsterCard2 = createMonsterCard2();
field.setMonster(1, monsterCard);
field.setMonster(3, monsterCard2);
assertEquals("Field([null, " + monsterCard.toString() + ", null, " + monsterCard2.toString() + ", null])", field.toString());
}
private MonsterCard createMonsterCard() {
return new MonsterCard("Test", 5, 5, MonsterModus.HEALING);
}
private MonsterCard createMonsterCard2() {
return new MonsterCard("zzz", 7, 7, MonsterModus.OFFENSIVE);
}
}
The relevant Arguments
and States
snippets:
public static int requirePositive(final int value, final String name) throws IllegalArgumentException {
Objects.requireNonNull(name);
if (value <= 0) {
throw new IllegalArgumentException("the " + name + " must be positive: " + value);
}
return value;
}
public static int requireIndexInRange(final int index, final int lowInclusive, final int highExclusive) throws IndexOutOfBoundsException {
if (index < lowInclusive || index >= highExclusive) {
throw new IndexOutOfBoundsException("the index must be in range: " + index + ", expected: [" + lowInclusive + ", " + highExclusive + ")");
}
return index;
}
public static <T, E extends RuntimeException> T requireNonNull(final T object, final Function<String, E> exceptionFunction, final String message) throws E {
Objects.requireNonNull(exceptionFunction);
Objects.requireNonNull(message);
if (object == null) {
throw exceptionFunction.apply(message);
}
return object;
}
A few notes:
- In the unit tests I explicitely create a
new Field(6)
every time, as the arguments are important to know. - The
static { assertTrue(true); }
is there such that Netbeans stop annoying me, it is standard in all my unit tests. - I use an experimental approach of providing a
Stream<E>
as view of a class as opposed to the Java 7-and-below standard ofCollection<E>
. The Java 8 developers encourage working with streams over the old collections API for writing API's.