As part of a Trading Card Game, I have created a Hand
that will need to hold all cards that a player currently has in his hand. The code is built using Java 8.
The structure is the following:
- Class for exception throwing.
- The Card interface.
- The Hand class.
- The HandTest test class.
public final class ExceptionUtils {
private ExceptionUtils() {
throw new UnsupportedOperationException();
}
public static <E extends RuntimeException> void throwOnFail(final BooleanSupplier resultSupplier, final Supplier<E> exceptionSupplier) throws E {
Objects.requireNonNull(resultSupplier);
throwOnFail(resultSupplier.getAsBoolean(), exceptionSupplier);
}
public static <E extends RuntimeException> void throwOnFail(final boolean result, final Supplier<E> exceptionSupplier) throws E {
Objects.requireNonNull(exceptionSupplier);
if (!result) {
throw exceptionSupplier.get();
}
}
public static <E extends RuntimeException> void throwOnFail(final BooleanSupplier resultSupplier, final Function<String, E> exceptionFunction, final String message) throws E {
Objects.requireNonNull(resultSupplier);
throwOnFail(resultSupplier.getAsBoolean(), exceptionFunction, message);
}
public static <E extends RuntimeException> void throwOnFail(final boolean result, final Function<String, E> exceptionFunction, final String message) throws E {
Objects.requireNonNull(exceptionFunction);
Objects.requireNonNull(message);
if (!result) {
throw exceptionFunction.apply(message);
}
}
public static <E extends RuntimeException> void throwOnSuccess(final BooleanSupplier resultSupplier, final Supplier<E> exceptionSupplier) throws E {
Objects.requireNonNull(resultSupplier);
throwOnSuccess(resultSupplier.getAsBoolean(), exceptionSupplier);
}
public static <E extends RuntimeException> void throwOnSuccess(final boolean result, final Supplier<E> exceptionSupplier) throws E {
Objects.requireNonNull(exceptionSupplier);
if (result) {
throw exceptionSupplier.get();
}
}
public static <E extends RuntimeException> void throwOnSuccess(final BooleanSupplier resultSupplier, final Function<String, E> exceptionFunction, final String message) throws E {
Objects.requireNonNull(resultSupplier);
throwOnSuccess(resultSupplier.getAsBoolean(), exceptionFunction, message);
}
public static <E extends RuntimeException> void throwOnSuccess(final boolean result, final Function<String, E> exceptionFunction, final String message) throws E {
Objects.requireNonNull(exceptionFunction);
Objects.requireNonNull(message);
if (result) {
throw exceptionFunction.apply(message);
}
}
}
public interface Card {
public String getName();
}
public class Hand {
private final List<Card> list = new ArrayList<>();
private final int capacity;
public Hand(final int capacity) {
ExceptionUtils.throwOnFail(capacity > 0, IllegalArgumentException::new, "capacity should be strictly positive");
this.capacity = capacity;
}
public boolean isFull() {
return (list.size() == capacity);
}
public void add(final Card card) {
Objects.requireNonNull(card);
ExceptionUtils.throwOnSuccess(this::isFull, IllegalStateException::new, "hand is full");
list.add(card);
}
public Card play(final int index) {
assertIndex(index);
return list.remove(index);
}
public void swap(final int indexOne, final int indexTwo) {
assertIndex(indexOne);
assertIndex(indexTwo);
Collections.swap(list, indexOne, indexTwo);
}
private void assertIndex(final int index) {
ExceptionUtils.throwOnFail(index >= 0 && index < list.size(), IndexOutOfBoundsException::new);
}
@Override
public String toString() {
return "Hand(" + capacity + ", " + list + ")";
}
}
public class HandTest {
{
assertEquals(true, true);
}
@Test
public void testConstructor() {
Hand hand = new Hand(1);
}
@Test(expected = IllegalArgumentException.class)
public void testConstructorIAE() {
Hand hand = new Hand(0);
}
@Test
public void testIsFull() {
Hand hand = new Hand(2);
hand.add(createCard());
assertEquals("hand should not be full", false, hand.isFull());
hand.add(createCard());
assertEquals("hand should be full", true, hand.isFull());
hand.play(1);
assertEquals("hand should not be full anymore", false, hand.isFull());
}
@Test
public void testAdd() {
Hand hand = new Hand(1);
hand.add(createCard());
}
@Test(expected = NullPointerException.class)
public void testAddNPE() {
Hand hand = new Hand(1);
hand.add(null);
}
@Test(expected = IllegalStateException.class)
public void testAddISE() {
Hand hand = new Hand(1);
hand.add(createCard());
hand.add(createCard());
}
@Test
public void testPlay() {
Hand hand = new Hand(1);
Card card = createCard();
hand.add(card);
assertEquals("card should be equal", card, hand.play(0));
}
@Test(expected = IndexOutOfBoundsException.class)
public void testPlayIOOB1() {
Hand hand = new Hand(1);
hand.play(-1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testPlayIOOB2() {
Hand hand = new Hand(1);
hand.play(0);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testPlayIOOB3() {
Hand hand = new Hand(1);
hand.add(createCard());
hand.play(1);
}
@Test
public void testSwap() {
Hand hand = new Hand(2);
Card card = createCard();
Card card2 = createCard2();
hand.add(card);
hand.add(card2);
hand.swap(0, 1);
assertEquals("card should be equal", card, hand.play(1));
assertEquals("card2 should be equal", card2, hand.play(0));
}
@Test(expected = IndexOutOfBoundsException.class)
public void testSwapIOOB1() {
Hand hand = new Hand(1);
hand.add(createCard());
hand.swap(-1, 0);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testSwapIOOB2() {
Hand hand = new Hand(1);
hand.add(createCard());
hand.swap(1, 0);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testSwapIOOB3() {
Hand hand = new Hand(1);
hand.add(createCard());
hand.swap(0, -1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testSwapIOOB4() {
Hand hand = new Hand(1);
hand.add(createCard());
hand.swap(0, 1);
}
@Test
public void testToString1() {
Hand hand = new Hand(1);
assertEquals("Hand(1, [])", hand.toString());
}
@Test
public void testToString2() {
Hand hand = new Hand(2);
Card card = createCard();
Card card2 = createCard2();
hand.add(card);
hand.add(card2);
assertEquals("Hand(2, [" + card + ", " + card2 + "])", hand.toString());
}
private Card createCard() {
return new MonsterCard("Test", 10, 100, MonsterModus.OFFENSIVE);
}
private Card createCard2() {
return new MonsterCard("Test2", 15, 150, MonsterModus.HEALING);
}
}
WIth the given tests I score a 100% instruction and branche coverage percentage on Hand
testing:
The assertEquals(true, true)
is there to ensure that Netbeans does not get rid of my static import there.
Special focus may be given on the ability to understand the code without javadoc.
I am aware that the ExceptionUtils class does not have unit tests yet.
Github repository for reference: https://github.com/skiwi2/TCG/
Card
s have the samename
? What if they are of the same type(c1.getClass()==c2.getClass()
)? Can aHand
containCard
s of different types? \$\endgroup\$Card
s. \$\endgroup\$add()
, which would be immutable can be a solution, that's also my understanding of what @palacsint mentions in his second answer. \$\endgroup\$