Introduction to the Law of Demeter

Alejandro Gervasio
Alejandro Gervasio
Published in
·Updated:

Share this article

SitePoint Premium
Stay Relevant and Grow Your Career in Tech
  • Premium Results
  • Publish articles on SitePoint
  • Daily curated jobs
  • Learning Paths
  • Discounts to dev tools

7 Day Free Trial. Cancel Anytime.

Key Takeaways

  • The Law of Demeter, also known as the Principle of Least Knowledge, promotes minimizing the knowledge an object has about other objects, advocating for interactions only with immediate neighbors to enhance modularity and maintainability.
  • Adhering to the Law of Demeter can significantly boost the design of loosely-coupled software modules, making the code easier to maintain, test, and modify.
  • Common violations of the Law of Demeter occur when objects or methods know too much about the structure and elements of other objects, leading to tightly coupled code that is hard to manage and evolve.
  • Practical examples in PHP demonstrate how violations can be subtly embedded in common practices, such as using service locators that expose internal details of other objects.
  • Refactoring code to comply with the Law of Demeter involves direct interactions with necessary components only, avoiding unnecessary intermediaries that complicate the architecture and increase dependency.
  • While the Law of Demeter enhances code quality by reducing dependencies and promoting high cohesion, it should be applied pragmatically, considering the specific context and potential trade-offs in complexity and performance.
Software programming is a balanced mix of art (sometimes a euphemism for improvisation) and a bunch of well-proven heuristics used to tackle certain problems and solve them in a decent fashion. Few will disagree that the artistic side is by far the hardest one to polish and distill over time. On the other hand, taming the forces behind the heuristics is fundamental for being able to developing software that rests on the foundation of good design. With so many heuristics stating how and why software systems should cling to a specific approach, it’s pretty disappointing not seeing a broader implementation of them in the world of PHP. For example, the Law of Demeter is probably one of the most underrated in the language’s realm. Effectively, the law’s “talk to your closest friends” mantra still seems to be in a pretty immature state in PHP, something that contributes to rot in the overall quality of several object-oriented code bases. Some popular frameworks are actively pushing it forward, trying to be more committed to the law’s commandments. Throwing blame around for infringing the Law of Demeter is pointless, as the best way to mitigate such breakages is to simply be pragmatic and understand what’s actually under the law’s hood hence consciously applying it when writing object-oriented code. In an attempt to join the just cause and dig a little bit deeper into the law from a practical point of view, in the next few lines I’ll be demonstrating through some hands-on examples why something so simple as adhering to the law’s principles can be a real boost when designing loosely-coupled software modules.

Knowing Too Much Isn’t a Good Thing

Often referred to as the Principle of Least Knowledge, the rules promoted by the Law of Demeter are easy to digest. Simply put, and assuming that you have a beautifully-crafted class which implements a given method, the method in question should be constrained to call other methods that belong to the following objects:
  1. An instance of the method’s originating class.
  2. Objects that are arguments of the target method.
  3. Objects that are created by the target method.
  4. Objects that are dependencies of the method’s originating class.
  5. Global objects (ouch!) that can be accessed by the originating class within the target method.
Although the list is a world away from being formal (for one that’s a little more formal, check out Wikipedia), the points are pretty easy to understand. In traditional design, the fact that an object knows way too much about another (and this implicitly includes knowing how to access a third one) is considered wrong because there are situations where the object has to unnecessarily traverse from top to bottom a clumsy mediator to find the actual dependencies it needs to work as expected. This is, for obvious reason, a serious design flaw. The caller has a pretty extensive and detailed knowledge about the mediator’s internal structure, even if this one is accessed through a few getters. Moreover, using an intermediary object to get to the one required by the caller makes a statement on its own. After all, why use such a tangled path to acquire a dependency or invoke one of its methods if the same result can be achieved by injecting the dependency directly? The process doesn’t make any sense at all. Let’s say we need to build up a file storage module which uses internally a polymorphic encoder to pull in and save data to a given target file. If we were intentionally sloppy and hooked up the module to an injectable service locator, its implementation would look like this:
<?php
namespace LibraryFile;
use LibraryDependencyInjectionServiceLocatorInterface;

class FileStorage
{
    const DEFAULT_STORAGE_FILE = "data.dat";
    private $locator;
    private $file;

    public function __construct(ServiceLocatorInterface $locator, $file = self::DEFAULT_STORAGE_FILE)  {
        $this->locator = $locator;
        $this->setFile($file);
    }
    
    public function setFile($file) {
        if (!is_readable($file) || !is_writable($file)) {
            throw new InvalidArgumentException(
                "The target file is invalid.");
        }
        $this->file = $file;
        return $this;
    }
    
    public function write($data) {
        try {
            return file_put_contents($this->file, 
                $this->locator->get("encoder")->encode($data),
                LOCK_EX);
        }
        catch (Exception $e) {
            throw new $e(
                "Error writing data to the target file: " . 
                $e->getMessage());
        }
    }
    
    public function read() {
        try {
            return $this->locator->get("encoder")->decode(
                @file_get_contents($this->file));
        }
        catch(Exception $e) {
            throw new $e(
                "Error reading data from the target file: " .
                $e->getMessage());
        }
    }
}
Leaving out of the picture some irrelevant implementation details, the focus is on the constructor of the FileStorage class and its write() and read() methods. The class injects an instance of a still undefined service locator, which is used later on for acquiring a dependency (the aforementioned encoder) in order to fetch and store data in the target file. This is a typical infringement of the Law of Demeter considering that the class first goes through the locator and in turn reaches the encoder. The caller FileStorage knows too much about the locator’s internals, including how to access the encoder, which definitively isn’t an ability I would sing praises about. It’s an artifact intrinsically rooted to the nature of service locators (and that’s why some see them as an anti-pattern) or any other kind of static or dynamic registries, something that I pointed out before. To have a more general view of the issue, let’s check the locator’s implementation:
<?php
namespace LibraryDependencyInjection;

interface ServiceLocatorInterface
{
    public function set($name, $service);
    public function get($name);
    public function exists($name);
    public function remove($name);
    public function clear();
}
<?php
namespace LibraryDependencyInjection;

class ServiceLocator implements ServiceLocatorInterface
{
    private $services = [];
    
    public function set($name, $service) {
        if (!is_object($service)) {
            throw new InvalidArgumentException(
                "Only objects can register with the locator.");
        }
        if (!in_array($service, $this->services, true)) {
            $this->services[$name] = $service;
        }
        return $this;
    }
    
    public function get($name) {
        if (!$this->exists($name)) {
            throw new InvalidArgumentException(
                "The requested service is not registered.");
        }
        return $this->services[$name];
    }
    
    public function exists($name) {
        return isset($this->services[$name]);
    }
    
    public function remove($name) {
        if (!$this->exists($name)) {
            throw new InvalidArgumentException(
                "The requested service is not registered.");
        }
        unset($this->services[$name]);
        return $this;
    }
    
    public function clear() {
        $this->services = [];
        return $this;
    }
}
In this case I implemented the locator as a plain dynamic registry with no additional bells or whistles so it’s easy to follow. You can decorate it with some extra functionality if you’re in the mood. The last thing we must do is create at least one concrete implementation of the corresponding encoder so that we can put the file storage class to work. This class should do the trick pretty nicely:
<?php
namespace LibraryEncoder;

interface EncoderInterface
{
    public function encode($data);
    public function decode($data);
}
<?php
namespace LibraryEncoder;

class Serializer implements EncoderInterface
{
    public function encode($data) {
        if (is_resource($data)) {
            throw new InvalidArgumentException(
                "PHP resources are not serializable.");
        }
        if (($data = serialize($data)) === false) {
            throw new RuntimeException(
                "Unable to serialize the data.");
        }
        return $data;
    }
    
    public function decode($data) {
        if (!is_string($data)|| empty($data)) {
            throw new InvalidArgumentException(
                "The data to be unserialized must be a non-empty string.");
        }
        if (($data = @unserialize($data)) === false) {
            throw new RuntimeException(
                "Unable to unserialize the data."); 
        }
        return $data;
    }
}
With the encoder set, now let’s get things rolling using all the sample classes together:
<?php
use LibraryLoaderAutoloader,
    LibraryEncoderSerializer,
    LibraryDependencyInjectionServiceLocator,
    LibraryFileFileStorage;
    
require_once __DIR__ . "/Library/Loader/Autoloader.php";

$autoloader = new Autoloader();
$autoloader->register();

$locator = new ServiceLocator();
$locator->set("encoder", new Serializer());

$fileStorage = new FileStorage($locator);
$fileStorage->write(["This", "is", "my", "sample", "array"]);

print_r($fileStorage->read());
The violation of the law is in this case a rather furtive issue hard to track down from the surface except for the use of the locator’s mutator which suggests that at some point the encoder will be accessed and consumed in some form by an instance of FileStorage. Regardless, we know the infringement is just right there hidden from the outside world, a fact that not only reveals too much about the locator’s structure, but couples unnecessarily the FileStorage class to the locator itself. Just by sticking to the law’s rules and getting rid of the locator, we’d be removing the coupling, while at the same providing FileStorage with the actual collaborator it needs to do its business. No more clunky, revealing mediators along the way! Fortunately, all this babble can be easily translated into working code with just a pinch of effort. Just check the enhanced, Law of Demeter-compliant version of the FileStorage class here:
<?php
namespace LibraryFile;
use LibraryEncoderEncoderInterface;

class FileStorage
{
    const DEFAULT_STORAGE_FILE = "data.dat";
    private $encoder;
    private $file;

    public function __construct(EncoderInterface $encoder, $file = self::DEFAULT_STORAGE_FILE)  {
        $this->encoder = $encoder;
        $this->setFile($file);
    }
    
    public function setFile($file) {
        // the sample implementation
    }
    
    public function write($data) {
        try {
            return file_put_contents($this->file, 
                $this->encoder->encode($data), LOCK_EX);
        }
        catch (Exception $e) {
            throw new $e(
                "Error writing data to the target file: " .
                $e->getMessage());
        }
    }
    
    public function read() {
        try {
            return $this->encoder->decode(
                @file_get_contents($this->file));
        }
        catch(Exception $e) {
            throw new $e(
                "Error reading data from the target file: " .
                $e->getMessage());
        }
    }
}
That was easy to refactor, indeed. Now the class directly consumes any implementers of the EncoderInterface interface, avoiding going through the internals of an unnecessary intermediate. The example is unquestionably trivial, but it does make a valid point and demonstrates why adhering to the Law of Demeter’s commandments is one of the best things you can do to improve the design of your classes. Still, there’s a special case of the law, covered in depth in Robert Martin’s book Clean Code: A Handbook of Agile Software Craftsmanship
, that deserves a particular analysis. Just think this through for a moment: what would happen if FileStorage was defined to acquire its collaborator via a Data Transfer Object (DTO), like this?
<?php
namespace LibraryFile;

interface FileStorageDefinitionInterface
{
    public function getEncoder();
    public function getFile();
}
<?php
namespace LibraryFile;
use LibraryEncoderEncoderInterface;

class FileStorageDefinition implements FileStorageDefinitionInterface
{
    const DEFAULT_STORAGE_FILE = "data.dat";
    private $encoder;
    private $file;
    
    public function __construct(EncoderInterface $encoder, $file = self::DEFAULT_STORAGE_FILE)  {
        if (!is_readable($file) || !is_writable($file)) {
            throw new InvalidArgumentException(
                "The target file is invalid.");
        }
        $this->encoder = $encoder;
        $this->file = $file;
    }
    
    public function getEncoder()  {
        return $this->encoder;
    }
    
    public function getFile()  {
        return $this->file;
    }
}
<?php
namespace LibraryFile;

class FileStorage
{
    private $storageDefinition;
    
    public function __construct(FileStorageDefinitionInterface $storageDefinition) {
        $this->storageDefinition = $storageDefinition;
    }
    
    public function write($data) {
        try {
            return file_put_contents(
                $this->storageDefinition->getFile(), 
                $this->storageDefinition->getEncoder()->encode($data),
                LOCK_EX
            );
        }
        catch (Exception $e) {
            throw new $e(
                "Error writing data to the target file: " . 
                $e->getMessage());
        }
    }
    
    public function read() {
        try {
            return $this->storageDefinition->getEncoder()->decode(
                @file_get_contents($this->storageDefinition->getFile())
            );
        }
        catch(Exception $e) {
            throw new $e(
                "Error reading data from the target file: " .
                $e->getMessage());
        }
    }
}
It’s definitely an interesting slant for implementing the file storage class as it now uses an injectable DTO for transferring and consuming internally the encoder. The question that begs answering is if this approach really violates the law. In a purist sense it does, as the DTO is unquestionably a mediator exposing its whole structure to the caller. However, the DTO is just a plain data structure which, unlike the earlier service locator, has no behavior at all. And precisely the purpose of data structures is… yes, to expose its data. This means that as long as the mediator doesn’t implement behavior (which is exactly the opposite to what a regular class does, as it exposes behavior while hiding its data), the Law of Demeter will remain neatly preserved. The following snippet shows how to use the FileStorage with the DTO in question:
<?php
$fileStorage = new FileStorage(new FileStorageDefinition(new Serializer()));
$fileStorage->write(["This", "is", "my", "sample", "array"]);

print_r($fileStorage->read());
This approach is a lot more cumbersome than just directly passing the encoder into the file storage class, but the example shows that some tricky implementations, which at first blush seem to be flagrant breakers of the law, are, in general, pretty harmless as long as they make use of data structures with no behavior attached to them.

Closing Thoughts

With a prolific variety of tangled, sometimes esoteric, heuristics making their way through OOP, it seems pointless to add just another one to the pile, which apparently doesn’t have any visible positive impact in the design of layer components. The Law of Demeter, though, is everything but a principle with little or no application in the real world. Despite of its flourishy name, the Law of Demeter is a powerful paradigm whose primary goal is to promote the implementation of highly-decoupled application components by eliminating any unnecessary mediators. Just follow its commandments, without falling into blind dogmatism of course, and you’ll see the quality of your code improve. Guaranteed. Image via Fotolia

Frequently Asked Questions (FAQs) about the Law of Demeter

What is the main principle behind the Law of Demeter?

The Law of Demeter, also known as the principle of least knowledge, is a design guideline for developing software. This principle suggests that an object should only communicate with its immediate neighbors and should not have knowledge about the inner workings of other objects. It promotes loose coupling and modular code, making the software easier to maintain and modify.

How does the Law of Demeter improve code quality?

The Law of Demeter improves code quality by reducing the dependencies between classes or objects. This makes the code more modular and easier to understand, test, and maintain. It also reduces the risk of breaking changes, as modifications in one part of the code are less likely to affect other parts.

Can the Law of Demeter be applied to functions as well as objects?

Yes, the Law of Demeter can be applied to functions as well. When applied to functions, it suggests that a function should only call other functions in its immediate scope. This means that a function should not call another function that is defined inside another function.

What are the potential drawbacks of applying the Law of Demeter?

While the Law of Demeter can improve code quality, it can also lead to a proliferation of wrapper methods, as each object can only communicate with its immediate neighbors. This can make the code more verbose and harder to understand. It can also potentially impact performance, as more method calls are required.

How does the Law of Demeter relate to the concept of encapsulation?

The Law of Demeter is closely related to the concept of encapsulation, a fundamental principle of object-oriented programming. Encapsulation involves hiding the internal state and functionality of an object, exposing only what is necessary. The Law of Demeter takes this a step further by suggesting that an object should not even be aware of the inner workings of other objects.

Can the Law of Demeter be applied to other programming paradigms besides object-oriented programming?

While the Law of Demeter originated in the context of object-oriented programming, its principles can be applied to other programming paradigms as well. For example, in functional programming, the Law of Demeter can be interpreted as a guideline to minimize the dependencies between functions.

How does the Law of Demeter compare to other design principles, like SOLID?

The Law of Demeter is similar to the SOLID principles in that both are aimed at improving the maintainability and modularity of code. However, while SOLID provides a comprehensive set of guidelines for object-oriented design, the Law of Demeter is more focused, dealing specifically with the interactions between objects or functions.

Are there any tools or techniques to help enforce the Law of Demeter?

There are various static analysis tools that can help enforce the Law of Demeter by detecting violations in the code. Additionally, test-driven development (TDD) can also encourage adherence to the Law of Demeter, as it promotes writing small, focused tests, which in turn encourages writing loosely coupled code.

How does the Law of Demeter affect the performance of code?

While the Law of Demeter can potentially lead to more method calls, which could impact performance, this is generally not a significant concern. The benefits of improved maintainability and modularity typically outweigh any minor performance costs. Furthermore, modern compilers and interpreters are often able to optimize away the extra method calls.

Is it always necessary to follow the Law of Demeter?

Like any design principle, the Law of Demeter is a guideline, not a hard rule. There may be situations where it makes sense to violate the Law of Demeter for the sake of clarity or efficiency. The key is to understand the trade-offs and make informed decisions.

Alejandro Gervasio is a senior System Analyst from Argentina who has been involved in software development since the mid-80's. He has more than 12 years of experience in PHP development, 10 years in Java Programming, Object-Oriented Design, and most of the client-side technologies available out there.

Expert

Share this article

Subscribe to our newsletter

Get the freshest news and resources for developers, designers and digital creators in your inbox each week

© 2000 – 2025 SitePoint Pty. Ltd.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.