Law of Demeter (LoD) – Wissenshäppchen #8
Law of Demeter (LoD) Um das Law of Demeter (Gesetz von Demeter) in
der Softwareentwicklung geht es im achten Wissenshäppchen. Inhalt
Das Law of Demeter sagt aus, dass eine Methode nur Zugriff auf die
folgenden Objekte/Methoden haben sollte: Andere Inst...
22 Minuten
Podcast
Podcaster
Beschreibung
vor 5 Jahren
Law of Demeter (LoD)
Um das Law of Demeter (Gesetz von Demeter) in der
Softwareentwicklung geht es im achten Wissenshäppchen.
Inhalt
Das Law of Demeter sagt aus, dass eine Methode nur Zugriff auf
die folgenden Objekte/Methoden haben sollte:
Andere Instanzmethoden in ihrer eigenen Klasse
Ihre Parameter
Methoden in Objekten, die sie selbst erzeugt
Globale Variablen
Eine einfache andere Definition lautet: Verkette keine
Methodenaufrufe („method chaining“). Oder: Verwende nicht mehr
als einen ., wenn du auf etwas zugreifst. Ein anderer Name für
das LoD ist „Prinzip des geringsten Wissens“ („principle of least
knowledge“).
Der Name stammt von Demeter, einer griechischen Göttin. Der
„Erfinder“ des Prinzips entwickelte in den 80er-Jahren ein System
mit diesem Namen.
Erklärung
Vorweg: Globale Variablen waren in den 80ern hip, heute nicht
mehr. Bitte verwende sie am besten gar nicht.
Je mehr „Punkte“ in einer Aufrufhierarchie auftreten, desto
mehr Kenntnis hat eine Komponente vom „Innenleben“ der
aufgerufenen Komponenten.
Je mehr Kenntnis eine Komponente über die von ihr verwendeten
Komponenten hat, desto stärker bindet sie sich an diese. Wenn
sich die genutzten Komponenten ändern, muss sie sich mit ändern
bzw. ist kaputt.
Das verletzt das Geheimnisprinzip und die Kapselung, nach der
wir eigentlich in der Objektorientierung arbeiten wollen.
Je mehr solcher Abhängigkeiten eine Methode hat, desto
schwerer ist sie zu verstehen und zu testen.
Code, der sich an das LoD hält, gilt auch als „schüchtern“,
weil er „nur mit seinen Freunden spricht“.
Beispielcode
Um an den Gesamtbeitrag einer Versicherungspolice zu kommen, ist
die Kenntnis über die darin enthaltenen versicherten Personen und
deren Tarife nötig. Da es sich dabei jeweils um Listen handelt,
ist sogar noch ein „Flachklopfen“ der Strukturen nötig. Der Code
sieht vielleicht cool aus, ist aber stark an die Strukturen
gekoppelt. Das heißt, ein Test müsste sehr viel Aufwand
betreiben, um die nötigen Strukturen zu schaffen (z.B. mit
Mocks).
class Tarif def initialize(name) @name = name end def beitrag
123.45 end end class VersichertePerson def initialize(name) @name =
name @tarife = [ Tarif.new("KFZ"), Tarif.new("Hausrat") ] end def
tarife @tarife end end class Versicherungspolice def initialize
@versicherte_personen = [ VersichertePerson.new("Stefan Macke"),
VersichertePerson.new("Klaus Meyer") ] end def versicherte_personen
@versicherte_personen end end # cool, aber schwer verständlich,
anfällig für Anpassungen und auch schwer zu testen puts
Versicherungspolice.new .versicherte_personen .flat_map { |vp|
vp.tarife } .inject(0){|summe,tarif| summe + tarif.beitrag }
Um dies zu vermeiden, können die einzelnen Klassen von ihren
inneren Strukturen abstrahieren und stellen simple
Zugriffsmethoden für die gewünschten Ergebnisse bereit. Der
finale Aufruf sieht nun schon fast langweilig aus. Dafür liegt
die fachliche Berechnungslogik da wo sie hingehört: in den
jeweiligen Klassen und nicht beim Aufrufer.
class Tarif def initialize(name) @name = name end def beitrag
123.45 end end class VersichertePerson def initialize(name) @name =
name @tarife = [ Tarif.new("KFZ"), Tarif.new("Hausrat") ] end def
beitragssumme @tarife.inject(0){|summe,tarif| summe + tarif.beitrag
} end end class Versicherungspolice def initialize
@versicherte_personen = [ VersichertePerson.new("Stefan Macke"),
VersichertePerson.new("Klaus Meyer") ] end def gesamtbeitrag
@versicherte_personen.inject(0){|summe,vp| summe + vp.beitragssumme
} end end # langweilig, aber einfach zu verstehen und zu testen
puts Versicherungspolice.new.gesamtbeitrag Vorteile
Der Code wird verständlicher, weil nicht mehrere Strukturen
bekannt sein müssen.
Fachliche Logik landet evtl. eher da, wo sie hingehört.
Anstatt externen Komponenten Zugriff auf die inneren Daten zu
geben, werden Abstraktionen geschaffen.
Die Komponenten sind unabhängiger voneinander und lassen sich
leichter testen.
Große Test-Setups mit vielen Mocks, die weitere Mocks
zurückgeben, lassen sich vermeiden.
Interne Änderungen an verwendeten Komponenten wirken sich
nicht auf deinen Code aus. Du hast also weniger
Anpassungsaufwand.
Nachteile
Wenn du die Regel exakt befolgen möchtest, musst du womöglich
recht viel Boilerplate-Code schreiben, z.B. simple Getter, die an
Instanzvariablen delegieren.
Wenn sich die verwendeten Strukturen selten ändern, ist der
zusätzliche Aufwand vermutlich unnötig.
Literaturempfehlungen
Zu diesem Wissenshäppchen gibt es ein separates Kapitel in einem
meiner absoluten Lieblingsbücher zur Softwareentwicklung: The
Pragmatic Programmer*. Es ist vor Kurzem in der 20-Jahre-Edition
neu aufgelegt worden, aber das Law of Demeter ist immer noch
Thema.
*
Links
Permalink zu dieser Podcast-Episode
RSS-Feed des Podcasts
Gesetz von Demeter – Wikipedia
Law Of Demeter
Demeter’s Law: Don’t talk to strangers!
Um das Law of Demeter (Gesetz von Demeter) in der
Softwareentwicklung geht es im achten Wissenshäppchen.
Inhalt
Das Law of Demeter sagt aus, dass eine Methode nur Zugriff auf
die folgenden Objekte/Methoden haben sollte:
Andere Instanzmethoden in ihrer eigenen Klasse
Ihre Parameter
Methoden in Objekten, die sie selbst erzeugt
Globale Variablen
Eine einfache andere Definition lautet: Verkette keine
Methodenaufrufe („method chaining“). Oder: Verwende nicht mehr
als einen ., wenn du auf etwas zugreifst. Ein anderer Name für
das LoD ist „Prinzip des geringsten Wissens“ („principle of least
knowledge“).
Der Name stammt von Demeter, einer griechischen Göttin. Der
„Erfinder“ des Prinzips entwickelte in den 80er-Jahren ein System
mit diesem Namen.
Erklärung
Vorweg: Globale Variablen waren in den 80ern hip, heute nicht
mehr. Bitte verwende sie am besten gar nicht.
Je mehr „Punkte“ in einer Aufrufhierarchie auftreten, desto
mehr Kenntnis hat eine Komponente vom „Innenleben“ der
aufgerufenen Komponenten.
Je mehr Kenntnis eine Komponente über die von ihr verwendeten
Komponenten hat, desto stärker bindet sie sich an diese. Wenn
sich die genutzten Komponenten ändern, muss sie sich mit ändern
bzw. ist kaputt.
Das verletzt das Geheimnisprinzip und die Kapselung, nach der
wir eigentlich in der Objektorientierung arbeiten wollen.
Je mehr solcher Abhängigkeiten eine Methode hat, desto
schwerer ist sie zu verstehen und zu testen.
Code, der sich an das LoD hält, gilt auch als „schüchtern“,
weil er „nur mit seinen Freunden spricht“.
Beispielcode
Um an den Gesamtbeitrag einer Versicherungspolice zu kommen, ist
die Kenntnis über die darin enthaltenen versicherten Personen und
deren Tarife nötig. Da es sich dabei jeweils um Listen handelt,
ist sogar noch ein „Flachklopfen“ der Strukturen nötig. Der Code
sieht vielleicht cool aus, ist aber stark an die Strukturen
gekoppelt. Das heißt, ein Test müsste sehr viel Aufwand
betreiben, um die nötigen Strukturen zu schaffen (z.B. mit
Mocks).
class Tarif def initialize(name) @name = name end def beitrag
123.45 end end class VersichertePerson def initialize(name) @name =
name @tarife = [ Tarif.new("KFZ"), Tarif.new("Hausrat") ] end def
tarife @tarife end end class Versicherungspolice def initialize
@versicherte_personen = [ VersichertePerson.new("Stefan Macke"),
VersichertePerson.new("Klaus Meyer") ] end def versicherte_personen
@versicherte_personen end end # cool, aber schwer verständlich,
anfällig für Anpassungen und auch schwer zu testen puts
Versicherungspolice.new .versicherte_personen .flat_map { |vp|
vp.tarife } .inject(0){|summe,tarif| summe + tarif.beitrag }
Um dies zu vermeiden, können die einzelnen Klassen von ihren
inneren Strukturen abstrahieren und stellen simple
Zugriffsmethoden für die gewünschten Ergebnisse bereit. Der
finale Aufruf sieht nun schon fast langweilig aus. Dafür liegt
die fachliche Berechnungslogik da wo sie hingehört: in den
jeweiligen Klassen und nicht beim Aufrufer.
class Tarif def initialize(name) @name = name end def beitrag
123.45 end end class VersichertePerson def initialize(name) @name =
name @tarife = [ Tarif.new("KFZ"), Tarif.new("Hausrat") ] end def
beitragssumme @tarife.inject(0){|summe,tarif| summe + tarif.beitrag
} end end class Versicherungspolice def initialize
@versicherte_personen = [ VersichertePerson.new("Stefan Macke"),
VersichertePerson.new("Klaus Meyer") ] end def gesamtbeitrag
@versicherte_personen.inject(0){|summe,vp| summe + vp.beitragssumme
} end end # langweilig, aber einfach zu verstehen und zu testen
puts Versicherungspolice.new.gesamtbeitrag Vorteile
Der Code wird verständlicher, weil nicht mehrere Strukturen
bekannt sein müssen.
Fachliche Logik landet evtl. eher da, wo sie hingehört.
Anstatt externen Komponenten Zugriff auf die inneren Daten zu
geben, werden Abstraktionen geschaffen.
Die Komponenten sind unabhängiger voneinander und lassen sich
leichter testen.
Große Test-Setups mit vielen Mocks, die weitere Mocks
zurückgeben, lassen sich vermeiden.
Interne Änderungen an verwendeten Komponenten wirken sich
nicht auf deinen Code aus. Du hast also weniger
Anpassungsaufwand.
Nachteile
Wenn du die Regel exakt befolgen möchtest, musst du womöglich
recht viel Boilerplate-Code schreiben, z.B. simple Getter, die an
Instanzvariablen delegieren.
Wenn sich die verwendeten Strukturen selten ändern, ist der
zusätzliche Aufwand vermutlich unnötig.
Literaturempfehlungen
Zu diesem Wissenshäppchen gibt es ein separates Kapitel in einem
meiner absoluten Lieblingsbücher zur Softwareentwicklung: The
Pragmatic Programmer*. Es ist vor Kurzem in der 20-Jahre-Edition
neu aufgelegt worden, aber das Law of Demeter ist immer noch
Thema.
*
Links
Permalink zu dieser Podcast-Episode
RSS-Feed des Podcasts
Gesetz von Demeter – Wikipedia
Law Of Demeter
Demeter’s Law: Don’t talk to strangers!
Weitere Episoden
5 Minuten
vor 4 Wochen
11 Minuten
vor 4 Monaten
8 Minuten
vor 4 Monaten
8 Minuten
vor 4 Monaten
10 Minuten
vor 5 Monaten
In Podcasts werben
Kommentare (0)