Law of Demeter or principle of least knowledge is a design guideline, that is using to make code more simple and stable.
- Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
- Each unit should only talk to his friends; don't talk to strangers.
- Only talk to your immediate friends.
m should only invoke the methods of the following kind of objects:
- O itself
- m's parameters
- Any objects created/instantiated with m
- O's direct component objects
- A global variable, accessible by O in the scope of m
Note: m should't invoke methods of a member object returned by another method.
this breaks law:
$a->$b->method();
this doesn't:
$a->method();
Wrong:
class Seller
{
private $priceCalculator = new PriceCalculator();
public function sell(ProductCollection $products, Wallet $wallet)
{
/** @var $actualSum Money */
$actualSum = $wallet->getMoney(); //ok, rule #2
/** @var $requiredSum Money */
$requiredSum = $this->priceCalculator->calculate($products); //ok, rule #4
if($actualSum->isBiggerThan($requiredSum)) { //wrong
$balance = $actualSum->substract($requiredSum); //wrong
$wallet->setMoney($balance);
}
else {
throw new Exception('Required sum is bigger than actual');
}
}
}
Right:
class Seller
{
private $priceCalculator = new PriceCalculator();
public function sell(ProductCollection $products, Money $moneyForProducts)
{
/** @var $requiredSum Money */
$requiredSum = $this->priceCalculator->calculate($products); //ok, rule #4
if($moneyForProducts->isBiggerThan($requiredSum)) { //ok, rule #2
return $moneyForProducts->substract($requiredSum); //ok, rule #2
}
else {
throw new Exception('Required sum is bigger than actual');
}
}
}
Formally correct, but has the same problems as the first example:
class Seller
{
private $priceCalculator = new PriceCalculator();
public function sell(ProductCollection $products, Wallet $wallet)
{
/** @var $actualSum Money */
$actualSum = $wallet->getMoney(); //ok, rule #2
/** @var $requiredSum Money */
$requiredSum = $this->priceCalculator->calculate($products); //ok, rule #4
$balance = $this->subtract($actualSum, $requiredSum); //ok, rule #1
}
private function subtract(Money $reduced, Money $subtracted): Money
{
if($reduced->isBiggerThan($subtracted)) { //ok, rule #2
return $reduced->substract($subtracted); //ok, rule #2
}
else {
throw new Exception('Subtracted summ is bigger than reduced');
}
}
}
Formally follow rules is not enough. You need to analyze methods, arguments, relations and calls inside class.
Pros (because of using nearest objects and calling with one arrow):
- Reduce coupling.
- Increases encapsulation.
- Increases single responsibility.
Cons:
- Need to use many wrapper methods.
- May to increase overhead.
LoD is not good in all cases. When using middle objects such a DTO (this object don't have logic inside), or ORM's relations, don't follow LoD is fine.