Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A factory method may violate the Law Of Demeter?

quoting from here: https://en.wikipedia.org/wiki/Law_of_Demeter

More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:[2]

  • O itself

  • m's parameters

  • Any objects created/instantiated within m

  • O's direct component objects

  • A global variable, accessible by O, in the scope of m

In particular, an object should avoid invoking methods of a member object returned by another method

so in details:

class O
{
    private $c;
    public function m($obj1)
    {
        $this->a(); // OK
        $obj1->a(); // OK
        (new C())->a(); // OK
        $c->a(); // OK
        $a = function() { };
        $a(); // OK
    }

    private function a() {}
}

now the 3rd law is questionable. So I newly created an object. But if I instead of:

(new C())->a();

I do:

$this->factory->createC()->a();

is it still valid? A regular class was instantized, just not by new but a factory. But hey! The Law said:

In particular, an object should avoid invoking methods of a member object returned by another method

by this rule, the factory method fails! Now what? Does it really fail?

like image 787
John Smith Avatar asked Nov 28 '18 22:11

John Smith


2 Answers

I don't think so.

Especially this:

Any objects created/instantiated within m

I would apply that to the factory as well. Even though strictly the object's constructor is called in the factory, the object is still constructed by, and especially for m. I would interpret the factory as a special kind of constructor, and look past the fact that you don't see a new keyword there.

Given the various important roles that factories play in software design (inversion of control is one of them), I think they are too valuable to let go. It's better to change your interpretation of this law, or of what a constructor is and use those factories when you want.

like image 55
GolezTrol Avatar answered Sep 30 '22 01:09

GolezTrol


Shouldn't too strict follow to rules which originate from the law of Demeter. There is a need to understand the sense and purpose of this law. Abidance of the law of Demeter helps us avoid excess dependencies of code on external classes and components. One of the principles of this law tells us the following:

Each unit should have only limited knowledge about other units: only units "closely" related to the current unit

In your example, O class knows C class in any case. Usage of a factory doesn't affect this fact. Somehow or another O class depends on C class and that dependency is unavoidable. It means that dependency is not excess. In fact dependency between C class and O class is a dependency between "closely" related units, therefore there isn't the violation of the law of Demeter if you use a factory.

As an example, let's imagine the following instance of code:

class O
{

   public function m()
   {
      $c = new C();
      $c->a();
   }
}

As you can see O class knows C class and has the dependency on it. The law of Demeter is not violated in this code. If you'll modify this example like:

class O
{

   protected function build()
   {
      return new C();
   }

   public function m()
   {
      $c = $this->build();
      $c->a();
   }
}

class O still will know C class and depend on it, the law of Demeter won't be violated in this code. In fact, we delegated the responsibility of object making to the factory method. If you'll modify this example like:

class Factory
{
   public function build()
   {
      return new C();
   }
}

class O
{

   /**
    * @var Factory
    */
   protected $factory;

   public function m()
   {
      $c = $this->factory->build();
      $c->a();
   }
}

We delegated the responsibility of object making to the factory object, but this fact had violated the law of Demeter because nothing will change in dependencies between O class and C class. As prev instances, O class knows C class and has the dependency on it. We have same dependencies in all of three instances.

like image 30
Maksym Fedorov Avatar answered Sep 30 '22 01:09

Maksym Fedorov