Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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 å tegne et sekvensdiagram, som illustrerer hvordan MemoryProgram bruker Memory underveis, trigget av interaksjon med brukeren.

PlantUML Macro
border1
actor bruker
MemoryProgram -> Memory: nextItem()
Memory --> MemoryProgram: 3
MemoryProgram -> bruker: "Element nr. 1 er 3"
MemoryProgram -> bruker: "Gjenta element nr. 1 av 1"
bruker --> MemoryProgram: 3
MemoryProgram -> Memory: acceptItem()
Memory --> MemoryProgram: ok og ferdig
MemoryProgram -> Memory: nextItem()
Memory --> MemoryProgram: 4
MemoryProgram -> bruker: "Element nr. 2 er 4"
MemoryProgram -> bruker: "Gjenta element nr. 1 av 2"
bruker --> MemoryProgram: 3
MemoryProgram -> Memory: acceptItem()
Memory --> MemoryProgram: ok, men ikke ferdig
MemoryProgram -> bruker: "Gjenta element nr. 2 av 2"
bruker --> MemoryProgram: 4
MemoryProgram -> Memory: acceptItem()
Memory --> MemoryProgram: ok og ferdig
MemoryProgram -> Memory: nextItem()
Memory --> MemoryProgram: 4
PlantUML Macro
border1
class MemoryProgram {
}
class Memory {
	expectedItems	// list of numbers 1-9
	acceptedCount	// position in list
	nextItem()		// adds item to list
	acceptItem()	// accepts (or rejects) item
}

MemoryProgram -right-> Memory: memory

 

 

PlantUML Macro
border1
PlantUML Macro
object Memory1 {
	expectedItems = []
	acceptedCount = 0
}
object Memory2 {
	expectedItems = [3]
	acceptedCount = 0
}
Memory1 -down-> Memory2: nextItem() => 3
object Memory3 {
	expectedItems = [3]
	acceptedCount = 1
}
Memory2 -down-> Memory3: acceptItem() => ok og ferdig
object Memory4 {
	expectedItems = [3, 4]
	acceptedCount = 0
}
Memory3 -down-> Memory4: nextItem() => 4
object Memory5 {
	expectedItems = [3, 4]
	acceptedCount = 1
}
Memory4 -down-> Memory5: acceptItem() => ok, men ikke ferdig
object Memory6 {
	expectedItems = [3, 4]
	acceptedCount = 2
}
Memory5 -down-> Memory6: acceptItem() => ok og ferdig
object Memory7 {
	expectedItems = [3, 4, 7]
	acceptedCount = 0
}
Memory6 -down-> Memory7: nextItem() => 7

 

 

Vi ser at Memory hovedsaklig tilbyr to tjenester: å generere I sekvensdiagrammet har vi satt navn på to tjenester som Memory-objektet må tilby: nextItem() genererer nye tall i sekvensen og å ta , og acceptItem() tar imot et nytt tall (fra brukeren) og sjekke sjekker 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. Klassediagrammet til høyre er oppdatere i henhold til dette, som angitt i klassediagrammet med henholdsvis attributtene expectedItems og acceptedCount.

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.for noen vil det være det meste effektive. Men er man fortsatt usikker på hvordan den interne tilstanden håndteres av metodene, så kan et objekttilstandsdiagram være nyttige, siden det illustrerer effekten av metodekall på den interne tilstanden, ikke bare sekvensen av kall. Et slikt diagram er vist til høyre for tilstandsdiagrammet, basert på samme sekvens av kall.

Når vi begynner å kode, har vi valget mellom tre strategier. 1) Ved topp-ned-koding så skriver en først MemoryProgram og lar detaljene i Memory bli avgjort av hva som gjør MemoryProgram enklest å skrive. 2) Ved bunn-opp-koding skriver en Memory først og tilpasser MemoryProgram deretter. 3) Med en blandet (hybrid) strategi jobber vi 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 da 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:Nå som vi vet hvordan hvert kall er ment å oppdatere den interne tilstanden, så kan vi skrive selve kodenHer er koden for Memory:

Code Block
languagejava
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 
    }
} 

...