LU09b - Zweiseitige Mehrfachbeziehung (n zu n)

Aus der realen Welt sind Mehrfachbeziehungen bestens bekannt. So kann eine Feriendestination von vielen Personen gebucht werden und umgekehrt kann eine Person viele Destinationen besuchen.

Diese Beziehung wird wie folgt im UML-Klassendiagramm dargestellt.

Abb: n:n Beziehung

Bei einer zweiseitigen Mehrfachbeziehung enthält jedes Objekt eine Liste mit Referenzen zu den verbundenen Objekten. Beim Hinzufügen (add) und Löschen (remove) von Beziehungen, müssen Sie immer beide Listen anpassen. Dabei muss verhindert werden, dass ein Endlos-Loop entsteht, wenn sie die add_person und add_destination-Methoden gegenseitig aufrufen.

Umsetzung in Python

Für unsere Umsetzung prüfen wir zuerst, ob das Objekt in der Liste vorhanden ist. Nur wenn dies der Fall ist, wird die Beziehung hinzugefügt bzw. gelöscht.

class HolidayDestination:
    def __init__(self, location):
        self._location = location
        self._people = []
 
    def __str__(self):
        output = f'People visiting {self._location}: '
        for person in self._people:
            output += f' {person._name},'
        output += '\n------'
        return output
 
    def add_person(self, person):
        if not person in self._people:
            self._people.append(person)
            person.add_destination(self)
 
    def remove_person(self, person):
        if person in self._people:
            self._people.remove(person)
            person.remove_destination(self)
 
 
class Person:
    def __init__(self, name):
        self._name = name
        self._destinations = []
 
    def __str__(self):
        output = f'{self._name} is visting'
        for destination in self._destinations:
            output += f' {destination._location},'
        output += '\n------'
        return output
 
    def add_destination(self, destination):
        if not destination in self._destinations:
            self._destinations.append(destination)
            destination.add_person(self)
 
    def remove_destination(self, destination):
        if destination in self._destinations:
            self._destinations.remove(destination)
            destination.remove_person(self)
 
 
def main():
    # let's start by creating some people and destinations
    maya = Person('Maya')
    thomas = Person('Thomas')
    jusuf = Person('Jusuf')
    valley = HolidayDestination('Death valley')
    atlantis = HolidayDestination('Atlantis')
 
    # now we let people visit the destinations
    maya.add_destination(valley)
    valley.add_person(thomas)
    print(valley)
    atlantis.add_person(maya)
    jusuf.add_destination(atlantis)
    print(atlantis)
    print(maya, thomas, jusuf)
 
    # try to add two duplicates
    jusuf.add_destination(atlantis)
    atlantis.add_person(maya)
    print(atlantis)
 
    # and now some people are cancelling their vacation
    valley.remove_person(thomas)
    print (valley)
    jusuf.remove_destination(atlantis)
    print (jusuf)
 
    # finally we try to remove non-existing relationships
    atlantis.remove_person(thomas)
    print(atlantis)
    jusuf.remove_destination(valley)
    print (jusuf)
 
if __name__ == '__main__':
    main()

Natürlich könnte man die Methoden auch so anpassen, dass sie als Parameter einen Index, einen Key und/oder ein Objekt akzeptieren.


René Probst, bearbeitet durch Marcel Suter