Versions Compared

Key

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

...

1-1-assosiasjoner er koblinger mellom objekter hvor ett objekt av en type bare kan ha én navngitt kobling til ett annet objekt av en annen eller samme type. Et eksempel er eksempel at en person kan ha kjæledyr, men bare kan være gift med én person (om gangen), så et Person-objekt kan bare ha én ekteskap-kobling til et annet Person-objekt. I dette tilfellet må det andre Person-objektet ha den samme koblingen tilbake. Merk at det samme Person-objektet kan godt ha andre koblinger til samme eller andre Person-objekter, men koblingen må hete noe annetett, og kjæledyret kan bare ha én eier. Person-klassen vil ha getPet- og setPet-metoder, mens Pet-klassen vil ha getOwner- og setOwner. Så kommer det viktige poenget: Hvis en person er koblet til kjæledyret, så må kjæledyret være koblet tilbake. Det betyr at hvis en kaller person.setPet(pet), så må den også implisitt kalle pet.setOwner(person) og omvendt, så både eier og kjæredyr kobles til den andre. Og da blir det lett komplisert kode... Heldigvis finnes det en generell løsning som vises under.

1-1-assosiasjoner kodes i Java med et enkelt felt av riktig type og med navn tilsvarende assosiasjonenrollen. Navngivingen av feltet er forsåvidt underordnet, siden det er innkapslingen i form av get- og set-metoder som er viktig. Her er diagrammet for en generell 1-1-assosiasjon og kode-malen for de to klassene som deltar i den:

PlantUML Macro
class Rolle1Klasse {
}
class Rolle2Klasse {
}

Rolle1Klasse "rolle1 0:1" -- "rolle2 0:1" Rolle2Klasse: assosiasjon


Code Block
languagejava
public class Rolle1Klasse {

	private Rolle2Klasse rolle2;

	public Rolle2Klasse getRolle2() {
		return rolle2;
	}
	public void setRolle2(Rolle2Klasse rolle2) {
		this.rolle2 = rolle2;
	}
}
Code Block
languagejava
public class Rolle2Klasse {

	private Rolle1Klasse rolle1;

	public Rolle1Klasse getRolle1() {
		return rolle1;
	}
	public void setRolle1(Rolle1Klasse rolle1) {
		this.rolle1 = rolle1;
	}
}

For å sette opp en gjensidig kobling, så må en opprette ett objekt av hver type og kalle dere respektive set-metoder med det andre objektet som argument:

PlantUML Macro
object "r1 : Rolle1Klasse" as r1 
PlantUML Macro
class Rolle1Klasse {
}
classobject "r2 : Rolle2Klasse" as r2 {
}

Rolle1Klasser1 --> "rolle1rolle2" r2
r2 --> "rolle2" Rolle2Klasse: assosiasjon


 

rolle1" r1


Code Block
languagejava
Rolle1Klasse r1 = new Rolle1Klasse();
Rolle2Klasse r2 = new Rolle2Klasse();
r1.setRolle2(r2);
r2.setRolle1(r1);

Med koden i klassene over, så må en altså selv sørge for konsistens, dvs. at hvis r1 er koblet til r2 gjennom rolle2, så er r2 koblet til r1 gjennom rolle1. Det er bedre om dette skjer automatisk, så en er sikret konsistens. Merk at dette bare gjelder når assosiasjonen er to-veis, noe den ikke alltid er.

Det skal altså være nok å kalle én set-metode for å sette opp begge koblingene (evt. koble begge av med null som argument). Hver set-metode må derfor sjekke koblingen den andre veien, også. Dette kan være litt fiklete å gjøre rett, så her er koden for det to set-metodene:

Code Block
// i Rolle1Klasse

public void setRolle2(Rolle2Klasse rolle2) {
	// sjekk om koblingen er riktig allerede
	if (this.rolle2 == rolle2) {
		return;
	}
	// husk den gamle og sett den nye
	Rolle2Klasse oldRolle2 = this.rolle2;
	this.rolle2 = rolle2;
	// hvis det var en kobling fra før, koble den andre fra
	if (oldRolle2 != null && oldRolle2.getRolle1() == this) {
		oldRolle2.setRolle1(null);
	}
	// hvis dette er en ny kobling, koble den andre til
	if (this.rolle2 != null) {
		this.rolle2.setRolle1(this);
	}
}
Code Block
// i Rolle2Klasse

public void setRolle1(Rolle1Klasse rolle1) {
	// sjekk om koblingen er riktig allerede
	if (this.rolle1 == rolle1) {
		return;
	}
	// husk den gamle og sett den nye
	Rolle1Klasse oldRolle1 = this.rolle1;
	this.rolle1 = rolle1;
	// hvis det var en kobling fra før, koble den andre fra
	if (oldRolle1 != null && oldRolle1.getRolle2() == this) {
		oldRolle1.setRolle2(null);
	}
	// hvis dette er en ny kobling, koble den andre til
	if (this.rolle1 != null) {
		this.rolle1.setRolle2(this);
	}
}

De to set-metodene samarbeider på en måte om å gjøre tilstanden konsistent. Vi ser at koden er de to metodene er like, bortsett fra at rollene er byttet om. Merk at innkapslingen er ivaretatt ved at begge bruker den andres set-metode.

Her er koden en får hvis den generelle løsningen brukes på person og kjæredyr-eksemplet:

Code Block
// i Person

private Pet pet;
 
public Pet getPet() {
    return pet;
}
public void setPet(Pet pet) {
	// sjekk om koblingen er riktig allerede
	if (this.pet == pet) {
		return;
	}
	// husk den gamle og sett den nye
	Pet oldPet = this.pet;
	this.pet = pet;
	// hvis det var en kobling fra før, koble den andre fra
	if (oldPet != null && oldPet.getOwner() == this) {
		oldPet.setOwner(null);
	}
	// hvis dette er en ny kobling, koble den andre til
	if (this.pet != null) {
		this.pet.setOwner(this);
	}
}
Code Block
// i Pet

private Person owner;
 
public Person getOwner() {
    return owner;
}
public void setOwner(Person owner) {
	// sjekk om koblingen er riktig allerede
	if (this.owner == owner) {
		return;
	}
	// husk den gamle og sett den nye
	Person oldOwner = this.owner;
	this.owner = owner;
	// hvis det var en kobling fra før, koble den andre fra
	if (oldOwner != null && oldOwner.getPet() == this) {
		oldOwner.setPet(null);
	}
	// hvis dette er en ny kobling, koble den andre til
	if (this.owner != null) {
		this.owner.setPet(this);
	}
}

...