Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

PlantUML Macro
class Memory {
	-List<?> possibleItems
	-List<?> expectedItems
	-int acceptedCount
	+Memory(Collection<?>)
	+int getItemCount()
	+int getAcceptedCount()
	+? nextItem()
	+Boolean acceptItem(?)
}

Hva skal ? erstattes med?

Vi ser at Memory-klassen nå inneholder en liste over alle mulige verdier kalt possibleItems, som elementene i expectedItems skal plukkes fra. Vi har også lagt til en konstruktør som krever at en oppgir samlingen av mulig verdier, når Memory-objekter opprettes.

Et viktig spørsmål er valg av typer for attributtene. Den generelle regelen er å bruke så generelle typer som mulig og velge grensesnitt-klasser fremfor implementasjoner, for å gi størst frihet ved kodingen. Derfor har vi valgt å bruke List fremfor Collection for både possibleItems og expectedItems. List utvider Collection med metoder for å hente ut elementer basert på posisjon, og dette er nyttig når vi skal velge ut tilfeldige elementer fra possibleItems i nextItem() og sammenligne et nytt element fra brukeren med siste element i expectedItems i acceptItem(...). Argumentet til Memory-konstruktøren er imidlertid av typen Collection, fordi vi kun trenger å gå sekvensielt gjennom det, når vi kopierer elementene inn i den interne lista.

...

Et alternativ er å gjøre Memory generisk ved å introdusere et typeparameter T og bruke typeparameteret i attributt-deklarasjonene, som vist under. Dette vil la oss spille med alle typer objekter, men hvert enkelt hukommelsesspill kan bare spilles med en bestemt type, som bestemmes (statisk) når Memory-objektet opprettes. En spesialisering med f.eks. String vil ha effekten at listene og argumentene blir spesialisert til String (der vi har brukt T), tilsvarende klassen under til høyre. Hvis vi da prøver å blande ulike typer, så vil det gi typefeil (i editoren og ved kjøring):

...

Her er fullstendig kode for klasser Memory-klassen og en oppdatert MemoryProgram-klasse:

Code Block
languagejava
collapsetrue
public class Memory<T> {
    private List<T> possibleItems;
    private List<T> expectedItems;
    private int acceptedCount;
    
    public Memory(Collection<T> possibleItems) {
        this.possibleItems = new ArrayList<>(possibleItems);
        expectedItems = new ArrayList<T>();
        acceptedCount = 0;
    }
    public Memory(T... possibleItems) {
        this(Arrays.asList(possibleItems));
    }
    
    public T nextItem() {
        int index = (int) (Math.random() * possibleItems.size());    // generate random index
        T nextItem = possibleItems.get(index);                        // look up value
        expectedItems.add(nextItem);    // add to sequence
        acceptedCount = 0;                // reset accepted counter
        return nextItem;                // return new item
    }
    public Collection<T> nextItems(int count) {
        Collection<T> items = new ArrayList<T>();
        while (count > 0) {
            items.add(nextItem());
        }
        return items;
    }
    
    public int getItemCount() {
        return expectedItems.size();
    }
    
    public int getItemsLeft() {
        return expectedItems.size() - acceptedCount;
    }
    public Boolean acceptItem(T item) {
        // is acceptItem called after sequence is completed
        if (acceptedCount >= expectedItems.size()) {
            return false;
        }
        // is the number input by the user correct
        if (! expectedItems.get(acceptedCount).equals(item)) {
            // if they are not the same, we indicate this by returning false object
            return Boolean.FALSE;
        }
        acceptedCount++;    // correct number, so increment counter
        // is this the last number
        if (acceptedCount == expectedItems.size()) {
            // return true object
            return Boolean.TRUE;
        }
        // otherwise return null, indicating correct value, but not finished
        return null;
    }
}

public class MemoryProgram {
    private void run() {
        Scanner scanner = new Scanner(System.in);
        do {
            Memory<Integer> memory = new Memory<Integer>(1, 2, 3, 4, 5, 6, 7, 8, 9);
            while (true) {
                int nextItem = memory.nextItem();
                System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
                System.out.println("Next item is " + nextItem);
                System.out.println("Please input " + memory.getItemCount() + " items");
                Boolean result = null;
                do {
                    int nextInt = scanner.nextInt();
                    result = memory.acceptItem(nextInt);
                } while (result == null);
                if (result == Boolean.FALSE) {
                    System.out.println("Wrong, game over");
                    break;
                }
            }
            System.out.println("Another game (true/false)?");
        } while (scanner.nextBoolean());
        scanner.close();
    }
    
    public static void main(String[] args) {
        new MemoryProgram().run();
    }
}

Her har vi lagt til to ekstra metoder i Memory, for å gjøre klassen litt mer fleksibel og enklere å bruke:

  • nextItems(int)-metoden gir muligheten til å utvide sekvensen med flere elementer om gangen
  • konstruktøren Memory(T...) bruker såkalte varargs og gjør det enklere å lage et Memory-objekt med et spesifikt sett mulige verdier

I versjon 3 brukes denne Memory-klassen i et hukommelsesspill med grafisk grensesnitt basert på JavaFX.