Klassediagrammer er en illustrasjon av innholdet i og sammenhengen mellom klasser, og fungerer som et supplement til tekstlig kode. Et klassediagram viser klasser som bokser, attributter og operasjoner som tekstlinjer inni boksene (i hver sine deler) og assosiasjoner og arv som streker med og uten piler. I tillegg annoteres assosiasjonsstreker med informasjon om navn og såkalt multiplisitet (også kalt kardinalitet). Figuren under viser et enkelt klassediagram med tilsvarende Java-kode til høyre.

Attributter og assosiasjoner

PersonString firstNameString familyNameString getFullName()CarString brandownershipowner: 1cars: *
class Person {
	String firstName;
	String familyName;
	String getFullName();
	Collection<Car> cars;
 }

class Car {
	String brand;
	Person owner;
}

Figuren lengst til venstre viser et klassediagram med to klasser. Person-klassen har to attributter og én operasjon/metode, mens Car-klassen har ett attributt. Person- og Car-klassene er knyttet sammen med en ownership-assosiasjon. ownership-assosiasjonen er annotert med "owner: 1" i Person-enden og "cars: *" i Car-enden, for å indikere hvordan assosiasjonen forstås fra hver ende:

  • Fra Person-enden kan en komme til Car-enden via cars-rollen, og *-tegnet indikerer at én Person-instans kan være knyttet til flere Car-instanser.
  • Fra Car-enden kan en komme til Person-enden via owner-rollen, og 1-tegnet indikerer at én Car-instans bare kan være knyttet til én Person-instans.

Diagrammet tilsvarer omtrent Java-koden som er vist til høyre. Vi ser at attributter og operasjoner i klassediagrammet tilsvarer felt og metoder i Java-koden. Assosiasjonene har ikke noe direkte Java-motstykke, men en finner igjen assosiasjonsrollene som felt som brukes som implementasjonsteknikk. Når en instans kan være koblet til flere andre instanser, så må en bruke Collection (evt. array, List eller Set) og ikke en enkel type.

Hvis en assosiasjon har et pilhode, så betyr det at den er enveis, dvs. at en ikke kan navigere fra objekter i pilhode-enden til objekter i den andre enden. I praksis betyr det at en utelater felt for å lagre referanser i klassen i pilhode-enden. Hvis f.eks. assosiasjonen i diagrammet hadde pekt fra Person til Car, så hadde ikke Car-klassen trengt et owner-felt.

Arv (extends og implements)

Arv gjør at nye klasser kan bygge på eller utvide andre klasser. I klassediagrammer angis det med en spesiell pil fra subklassen til superklassen, med et litt stort pilhode som ikke er fylt. En bruker stiplet linje hvis det er snakk om implementasjon av grensesnitt, og ikke vanlig arv mellom klasser (eller mellom grensesnitt).

AbstractObservablevoid addObserver(Observer)void removeObserver(Observer)void fireUpdate();ConcreteObservableObservervoid update(AbstractObservable)ConcreteObserverobservers*
interface Observer {
	public void update();
}

abstract class AbstractObservable {

	private Collection<Observer> observers = new ArrayList<Observer>();

	public void addObserver(Observer observer) {
		observers.add(observer);
	}	

	public void removeObserver(Observer observer) {
		observers.remove(observer);
	}

	protected void fireUpdate() {
		for (Observer observer: observers) {
			observer.update(this);
		}
	}
}

class ConcreteObservable extends AbstractObservable {
}

class ConcreteObserver implements Observer {
	public void update(AbstractObservable) {
		// method body
	}
}

Figuren lengst til venstre viser et klassediagram for Observatør-observert-teknikken. Den abstrakte klassen AbstractObservable implementerer støtte for lagring og registrering av lyttere av typen Observer vha. assosiasjonen observers og metoden addObserver og removeObserver. Den konkrete (ikke-abstrakte) observerbare klassen ConcreteObservable arver dette feltet og disse metodene fra AbstractObservable. Observer-grensesnittet fanger opp hva det vil si å observere (være en lytter), nemlig å implementere update-metode som reagerer på endringen i det observerte objektet. Den konkrete klassen ConcreteObserver implementerer dette grensesnittet og dermed også update-metoden.

Vi har her angitt synlighet(smodifikatorer) for metodene: Grønn sirkel tilsvarer public og gul ruter tilsvarer protected (og rød firkant tilsvarer private, men det er ikke vist).

Den tilsvarende koden er vist under diagrammet. Vi ser at en heltrukken arvingspil tilsvarer extends og en stiplet arvingspil tilsvarer implements. Feltinitialiseringen og innholdet i metodene er ikke med i diagrammet og er med for kompletthet.

Merk at iht. navnkonvensjonen i Java så brukes Listener som endelse på observatør-grensesnitt og dermed også på felt, metoder og variabler som har med den å gjøre, altså XXXListener, Collection<XXXListener> xxxListeners, addXXXListener og removeXXXListener.

 


Oppbygningen til et klassediagram

Et klassediagram består av bokser som representerer klasser, og piler/linjer som representerer assosiasjoner og arv.

Boksene som representerer klassene er delt inn i tre deler:

  • Den øverste delen der navnet på klassen står
  • Den midterste delen der attributtene til klassen står
  • Den nederste delen der metodene til klassen står




ClassNameString attribute1int attribute2Collection<String> andSoOnString updateAttribute1(String)void addToCollection(String)

Mellom boksene kan man ha streker og/eller piler som representerer relasjonene mellom klassene. De vanligste er:

  • Arv: representeres med en lukket pil, der subklassen peker på superklassen.

Vehicleint speedint passengersString fuelTypeboolean isCorrectFuel(String)CarString brandint doors

  • Implementasjon av grensesnitt: representeres med stiplet linje og lukket pil, der klassen som implementerer et grensesnitt peker på grensesnittet som implementeres.

DatabaseEvoid add(E)void remove(E)boolean contains(E)BookDatabaseBookMap<Book, Integer> allBooksint getNumberOfBooks()

  • Toveis assosiasjon: begge klassene vet om hverandre (dvs. at de har en referanse til hverandre). Toveis assosiasjon representeres med en linje mellom to bokser (evt. mellom samme boks om referansen er til et eller flere objekt av samme klasse). På hver ende av linjen står det en rolle og kardinaliteten til referansen på formen "<rolle>: <kardinalitet>". Kardinaliteten kan være representert som ett tall eller *-symbolet. 

BookString titleint pagesPersonboolean bookLoverboolean hasRead(Book)boolean hasBook(Book)prequel: 1sequel: 1owner: 1books: *

Delvis kode til klassediagrammet
class Person{
	Map<Book, Boolean> books = new HashMap<>();
		// Boolean = true => Person has read book
		// Boolean = false => Person has not read book
	boolean bookLover;
	
	boolean hasRead(Book book){
		...
	}
	
	boolean hasBook(Book book){
		...
	}
}


class Book{
	String title;
	int pages;
	Person owner;
	Book sequel;
	Book prequel;
}


  • Enveis assosiasjon: en klasse vet om en annen klasse og interagerer med den, men denne klassen har ingen referanser til den første og "vet" dermed ikke om den. Enveis assosiasjoner representeres med en åpen pil som peker fra klassen med referansen til klassen som blir referert.

BookString titleint pagesPersonboolean bookLoverboolean hasRead(Book)boolean hasBook(Book)prequel: 1books: *

Delvis kode til klassediagrammet
class Person{
	Map<Book, Boolean> books = new HashMap<>();
		// Boolean = true => Person has read book
		// Boolean = false => Person has not read book
	boolean bookLover;
	
	boolean hasRead(Book book){
		...
	}
	
	boolean hasBook(Book book){
		...
	}
}


class Book{
	String title;
	int pages;
	Book prequel;
}


Synlighetsmodifikatorer for attributter og metoder har følgende symboler:

  • Public: grønn sirkel
  • Protectedgul ruter/diamant
  • Privaterød firkant

De kan også representeres med følgende symboler:

  • Public: +
  • Protected: #
  • Private: -





VisibilitypublicAttributeprotectedAttributeprivateAttributepublicMethode()protectedMethode()privateMethode()