The Law of Demeter, also called the Principle of Least Knowledge, minimizes dependencies by making a class know as little as possible about other classes’ implementations. Encapsulation hides changeable details behind public operations, so implementation changes do not propagate unnecessarily. section 5.1 section 4.2.2
Allow a method to call another object when the object is close to the method:
- Call a method on an object aggregated by the current class. section 5.6
- Call a method on an object passed as the current method’s argument. section 5.6
- Call a method on an object instantiated by the current method. section 5.6
- Avoid calling a method on an object returned by another object’s method; that creates a dependency on a farther-away class. section 5.6
Servicing an automobile
In the DemeterAuto example, maintain_auto() safely calls self._engine.replace_sparkplug() because Engine is an aggregated object and exposes the operation it owns. It also safely calls replace() on a passed plug or a newly created Sparkplug. By contrast, retrieving self._engine.sparkplug and then calling plug1.replace() reaches through Engine to its internal object and violates the law. Prefer Engine.replace_sparkplug() over exposing the sparkplug and its implementation. section 5.6
Listing 5.18 (Program 5.7 DemeterAuto): auto.py
class Sparkplug:
def __init__(self, name):
self._name = name
def replace(self):
print(f'Replaced sparkplug {self._name}')
class Engine:
def __init__(self, sparkplug):
self._sparkplug = sparkplug
@property
def sparkplug(self): return self._sparkplug
def replace_sparkplug(self):
self._sparkplug.replace() #1
class DemeterAuto:
def __init__(self, engine):
self._engine = engine
def service_sparkplug(self, plug):
plug.replace() #2
def maintain_auto(self):
self._engine.replace_sparkplug() #3
plug1 = self._engine.sparkplug
plug1.replace() #4
plug2 = Sparkplug('plug2')
plug2.replace() #5
Compare compliant and noncompliant calls in the DemeterAuto, Engine, and Sparkplug implementation. section 5.6

