...
Det som ikke kommer frem av dialogen er hvordan MemoryProgram-objektet samspiller med Memory-objektet, dvs. hvilke kall MemoryProgram utfører på Memory og hvilke verdier som flyter frem (argumentverdier) og tilbake (returverdier). Dette er den vanskeligste delen av programmering: å bestemme når og hvordan objektene samhandler, altså å fordele oppgaver mellom objektene og konkretisere oppgavene som metoder med argumenter og returverdier. En måte å komme litt videre på er å legge inn kommentarer i dialogen overtegne et sekvensdiagram, som illustrerer hvordan MemoryProgram bruker Memory underveis. Merk at dette ikke er så ulikt et sekvensdiagram, men det er litt tidlig å bruke en så formell notasjon, siden vi ennå ikke har satt navn på metoder og gitt dem argument- og returtyper., trigget av interaksjon med brukeren.
|
| // Memory genererer nytt tall i sekvensen => 3
Vi ser at Memory hovedsaklig tilbyr to tjenester: å generere nye tall i sekvensen og å ta imot et nytt tall (fra brukeren) og sjekke det mot fasiten. Det er dessuten underforstått at Memory må huske både tall-sekvensen og hvor langt brukeren har kommet i å gjenta den. Vi kan nå oppdatere diagrammet vårt med denne innsikten:
...
Klassediagrammet til høyre er oppdatere i henhold til dette.
Dette er et godt utgangspunkt for å begynne å kode, og selv om det er mulig å fylle ut mer detaljer i diagrammet først, så er erfaringen at koding raskere skaper fremdrift ved å avdekke problemer og mulige løsninger.
Vi har valget mellom tre strategier: 1) skrive MemoryProgram og dermed avdekke mer presist hvilke metoder Memory må tilby, 2) skrive Memory først og tilpasse MemoryProgram deretter, eller 3) en hybrid strategi hvor vi jobber parallelt med begge to. Dette er ofte en smaksak, men jeg foretrekker ofte å jobbe topp-ned på skissestadiet, som vi er på nå, og bunn-opp med selve kodingen. Imidlertid kan en godt jobbe litt mer på papir, for å klargjøre i litt mer detalj hva hver metode er ment å gjøre, og her kan både sekvensdiagrammer og objekttilstandsdiagrammer være nyttige. Førstnevnte illustrerer hvordan objekter utveksler data i metodekall, trigget av interaksjon med brukerenda kan et objekttilstandsdiagram være nyttige, siden det illustrerer effekten av metodekall på den interne tilstanden, ikke bare sekvensen av kall. Her er et slikt diagram basert på eksemplet over:
PlantUML Macro |
---|
actorstate Memory1 bruker MemoryProgram -> Memory: genererNesteTall() Memory --> MemoryProgram: 3 MemoryProgram{ expectedItems = [] acceptedCount = 0 } state Memory2 { expectedItems = [3] acceptedCount = 0 } Memory1 -> brukerMemory2: "Element nr. 1 ernextItem() => 3" MemoryProgramstate -> bruker: "Gjenta element nr. 1 av 1" bruker --> MemoryProgram: 3 MemoryProgramMemory3 { expectedItems = [3] acceptedCount = 1 } Memory2 -> MemoryMemory3: sjekkNesteTallFraBrukeracceptItem() Memory --=> MemoryProgram: ok og ferdig MemoryProgramstate -> Memory: genererNesteTall() Memory --> MemoryProgram: 4 MemoryProgramMemory4 { expectedItems = [3, 4] acceptedCount = 0 } Memory3 -> brukerMemory4: "Element nr. 2 ernextItem() => 4" MemoryProgramstate -> bruker: "Gjenta element nr. 1 av 2" bruker --> MemoryProgram: 3 MemoryProgramMemory5 { expectedItems = [3] acceptedCount = 1 } Memory4 -> MemoryMemory5: sjekkNesteTallFraBrukeracceptItem() Memory --=> MemoryProgram: ok, men ikke ferdig MemoryProgramstate -> bruker: "Gjenta element nr. 2 av 2" bruker --> MemoryProgram: 4 MemoryProgramMemory6 { expectedItems = [3, 4] acceptedCount = 2 } Memory5 -> MemoryMemory6: sjekkNesteTallFraBrukeracceptItem() Memory --=> MemoryProgram: ok og ferdig MemoryProgram state Memory7 { expectedItems = [3, 4, 7] acceptedCount = 0 } Memory6 -> MemoryMemory7: genererNesteTallnextItem() Memory --=> MemoryProgram: 4 |
7
|
Nå som vi vet hvordan hvert kall er ment å oppdatere den interne tilstanden, så kan vi skrive selve kodenHer er kode for å representere tallsekvensen, generere et nytt tall, huske hvor langt i sekvensen brukeren har kommet og sjekke et nytt tall mot riktig tall i sekvensen:
Code Block | ||
---|---|---|
| ||
public class Memory { private List<Integer> expectedItems = new ArrayList<Integer>(); private int acceptedCount = 0; public int nextItem() { int nextItem = (int) (Math.random() * 9) + 1; // new number value expectedItems.add(nextItem); // add to number sequence acceptedCount = 0; // reset counter return nextItem; // return new number value } public Boolean acceptItem(int item) { if (! expectedItems.get(acceptedCount).equals(item)) { // compare the number input by the user with the corresponding sequence value return Boolean.FALSE; // if they are not the same, we indicate this by returning false } acceptedCount++; // correct number, so increment counter if (acceptedCount == expectedItems.size()) { // if this was the last number return Boolean.TRUE; // return true } return null; // otherwise return null, indicating correct value, but not finished with sequence } } |
...