Law of Demeter
22 Sep 2016 in Programming, PHP, Software Architecture
The Law of Demeter or principle of least knowledge is a style guideline for developing software.
According to Virtuous Code:
For all classes C. and for all methods M attached to C, all objects to which M sends a message must be instances of classes associated with the following classes:
The argument classes of M (including C). The instance variable classes of C.
(Objects created by M, or by functions or methods which M calls, and objects in global variables are considered as arguments of M.)
or in plain english:
- Your method can call other methods in its class directly
- Your method can call methods on its own fields directly (but not on the fields' fields)
- When your method takes parameters, your method can call methods on those parameters directly.
- When your method creates local objects, that method can call methods on the local objects.
This translates to not having a chain of method calls like $someObject->foo()->bar()->baz()
somewhere in your code.
For a simple example, consider the following:
/**
* Represent a {lat,lng} pair
*/
class LatLng {
private $lat;
private $lng;
public function __construct($lat, $lng) {
$this->lat = $lat;
$this->lng = $lng;
}
public function lat() {
return $this->lat;
}
public function lng() {
return $this->lng;
}
}
/**
* Represent a Line/Pair of LatLng's
*/
class Line {
private $start;
private $end;
public function __construct(LatLng $start, LatLng $end) {
$this->start = $start;
$this->end = $end;
}
public function start() {
return $this->start;
}
public function end() {
return $this->end;
}
public function distance() {
// calculate and return the distance between start and end
}
}
$start = new LatLng(...);
$end = new LatLng(...);
$line = new Line($start, $end);
// ...more code...
$startLat = $line->start()->lat();
Law of Demeter states that you should avoid the above chain of calls.
This can be accomblished by rewriting the Line
class like this:
/**
* Represent a Line/Pair of LatLng's
*/
class Line {
private $start;
private $end;
public function __construct(LatLng $start, LatLng $end) {
$this->start = $start;
$this->end = $end;
}
public function startLat() {
return $this->start->lat();
}
public function startLng() {
return $this->start->lng();
}
public function endLat() {
return $this->end->lat();
}
public function endLng() {
return $this->end->lng();
}
public function distance() {
// calculate and return the distance between start and end
}
}
// and now we can write
$start = new LatLng(...);
$end = new LatLng(...);
$line = new Line($start, $end);
// ...more code...
$startLat = $line->startLat();
But...
But, this example is not entirelly correct as outlined in this answer from stack overflow (How to obey Law of Demeter?):
You are not applying LoD at the appropriate level: both Band and Guitarist should be considered a part of the same module and the dilemma you have shall be decided on the grounds of maximum convenience.
Your question is an example of a much wider problem, which I have frequently met in books on design patterns and similar: they try to explain wide-reaching principles, which concern the design of a complex system, on ridiculously undersized problems. The result is just reader's confusion.
Where you'd actually see this principle in effect is something like this: you are using AsyncHttpClient, which is an abstraction built atop Netty, which is an abstraction built atop Java NIO. Now, if AsyncHttpClient's API forced you at some place to directly manage a Java NIO object, whose API is much more raw, and deals with concepts completely foreign to AsyncHttpClient, that would be an example of breaking LoD.
More links: