Noen objekter har hovedsaklig som formål å huske og håndtere data, og disse kaller vi data- eller tilstandsorienterte.

Et eksempel på dette er et Person-objekt som hånderer data tilsvarende det en finner på et visittkort. Et slik objekt gjør ikke så mye mer enn å lagre attributt-verdier, og tilbyr (stort sett bare) operasjoner for å lese og endre attributtene.

Enkle verdier

Anta at Person-objektet håndterer verdier for navn, fødselsdato og e-post-adresse. Hvert Person-objekt vil ha unike verdier, som leses og settes med egne operasjoner. Før en definerer disse operasjonene må en gi verdiene navn og bestemme hvilken datatype som er logisk for hvert av dem. Datatypene vil vi finne igjen i parameter- og returntypene for getter- og setter-operasjonene. F.eks. kan vi kalle og bruke fullName og String for navnet til personen og det vil gi opphav til operasjonene String getFullName() og void setFullName(String). En mulig innkapsling av disse verdiene er vist under til venstre.

Innkapsling av navn, fødselsdato og e-postPersonString getFullName()void setFullName(String)Date getDateOfBirth()void setDateOfBirth(Date)String getEmail()void setEmail(String)Implementasjon av navn, fødselsdato og e-postPersonString nameDate dateOfBirthString emailUserString emailDomainString getFullName()void setFullName(String)Date getDateOfBirth()void setDateOfBirth(Date)String getEmail()void setEmail(String)

Merk at vi kan velge andre navn og typer til attributtet som brukes til å lagre verdien, for å gjøre det mer praktisk å bruke verdien internt i klassen. F.eks. kan vi ha to attributter for lagre e-post-adresser, ett for brukernavnet og ett for domenet/tjenesten, selv om vi har valgt å kapsle dem inn e-post-verdien med operasjonene String getEmail() og void setEmail(String). Dette kan gjøre koden internt i klassen enklere, selv om det kan gjøre implementasjon av operasjonene litt mer komplisert. Implementasjonen kan være som vist over til høyre.

Multiple verdier

Håndtering av attributter for enkeltverdier er nokså enkelt, men når ett attributt tilsvarer mange verdier, så blir det mer komplisert, kanskje mest fordi det er flere valg å ta omkring både innkapslingsmetoder og implementasjon av attributtene. Ta som eksempel håndtering av barna til en person. Siden en kan ha flere av dem, så kan en ikke innkapsle dem med Person getChild() og setChild(Person). En variant er å bruke tabeller, altså Person[] getChildren() og void setChildren(Person[]), men det vil gjøre enkle operasjoner som å legge til barn mer komplisert å kode for de som bruker Person-klassen. Derfor er det bedre å velge en innkapsling som er mer skreddersydd for bruken av dataene. Tre forslag er vist under:

Innkapsling av barn variant 1Personboolean containsChild(Person)Iterator<Person> getChildren()void addChild(Person)void removeChild(Person)Innkapsling av barn variant 2Personint getChildCount()int indexOfChild(Person)Person getChild(int)void addChild(Person)void removeChild(Person)Innkapsling av barn variant 3Personint getChildCount()int indexOfChild(Person)Person getChild(int)void addChild(int, Person)void removeChild(int)

Variant 1 gir mulighet til å sjekke om en person har en annen person som barn, gå gjennom alle barna vha. en såkalt iterator (se Iterator) og legge til og fjerne barn. Merk at ingen metoder knytter nummer til barna, så skal en hente ut barn nr. 3, så må en be om alle og hoppe over de to første (om de finnes). Variant 2 gjør det enklere å hente ut barn basert på indeks (nummer i rekka), men ikke endre på rekkefølgen, slik variant 3 gir mulighet for. Hvilken av disse variantene en bør velge er avhengig av hva det er naturlig for andre klasser å gjøre med person-barn-koblingen. For andre typer koblinger kan det være begrensninger som gjør at noen metoder utelates eller en forventet bruk som gjør at en tilbyr et rikere utvalg, f.eks. en kombinasjon av flere varianter. Det vesentlige er 1) at en vurderer hvilke logiske operasjoner en er tjent med og 2) at en følger etablerte konvensjoner for navngiving og signaturer.

Standardoperasjoner versus logiske operasjoner

Når en skal innkapsle verdier, er det lettvint å bruke konvensjoner for navngiving og signaturer, slik vi har gjort over. Men en skal være forsiktig med å gjøre dette ukritisk, siden det ikke alltid stemmer med den iboende logikken til og bruken av verdiene. Ta en konto som eksempel, som kan ses på som en innkapsling av et beløp eller balanse. Det er fort gjort å definere en innkapsling som vist under til venstre, men er dette riktig? Det er riktignok naturlig å kunne spørre om balansen, men en setter jo sjelden balansen direkte. I stedet setter en inn og tar ut beløp, altså legger til eller trekker fra. Noen ganger påløper det ulike gebyr for hver av disse, så det er også en grunn til å skille dem fra hverandre. Derfor er innkapslingen under til høyre bedre.

Innkapsling med standardoperasjonerAccountint getBalance()void setBalance(int)Innkapsling med tilpassede operasjonerAccountint getBalance()void deposit(int)int withdraw(int)