5

According to following article Named Constructors The Author suggests using static factory pattern to construct objects is way better than instantinate with new keyword. At the begining the idea is appealing(according to some details in article) yes method names are more obvious and it gives a sense that the construction of the class it is not dependent to it's contstructor but class has different behaviours according to different problems on construction level.

<?php
$customer = new Customer($name); 
// We can't "new a customer" or "instantiate a customer" in real life.
// Better:
$customer = Customer::fromRegistration($name);
$customer = Customer::fromImport($name);

But the problem I see here according to this idiom we are using class methods like functions! And according to the above example it creates coupling issues we have to wrap this static factories with an injectible factory to decouple from the rest of the application?

Could you please clarify me to understand this. I started to see this idiom all around in PHP community and even in some Java examples.

12
  • 6
    "we are using class methods like functions!". You say this like it's a bad thing... Oct 3, 2017 at 9:05
  • 16
    "We can't "new a customer" or "instantiate a customer" in real life." what a nonsense argument. Code is not real life. Oct 3, 2017 at 9:09
  • 3
    It looks like the motivation is to work around PHP's restriction on only one constructor. It is orthogonal to coupling issues around newing objects (I.e. new has to appear somewhere)
    – Caleth
    Oct 3, 2017 at 9:09
  • 2
    @CodesInChaos The author is not only one thinking it. You are correct that code is not real life, but DDD code SHOULD be using ubiquitous language the business understands, too. newing a customer makes no sense to the business. Creating a customer from registration session does. I see the motivation there.
    – Andy
    Oct 3, 2017 at 10:22
  • 1
    Note : even if you can have multiple constructors, if you have two possibilities : one from name, another from registration, and they're both String, you're stucked. Either you need to create a wrapper class for one of them, or you need static method/factories. Problem is, you can't use inheritance and override static methods.
    – Walfrat
    Oct 3, 2017 at 11:10

3 Answers 3

10

Named Constructors in PHP

Don't limit yourself by PHP's single constructor. Use static factory methods.

The article is not about coupling. There is zero difference in coupling between using new Customer(/* some args */) and Customer::fromFoo(/* some args */). Where you put those expressions determines how coupled they are. All the "Factory Pattern" boils down to is "expression returning object".

The difference is instead about working around PHP's restriction on a class having a single constructor. If you want to have multiple ways of constructing a class, there are two choices:

  • distinguish them in the single constructor, from the arguments
  • distinguish them with multiple static factories, which take different arguments from each other.
3
  • I got your point here so then when you need to pass this to DI your container have to support object instantination through static factory methods. With that you can still pass things through DI.
    – FZE
    Oct 3, 2017 at 12:38
  • function ($args) { return new Customer($args) } is a value of type args_type -> Customer. Customer::fromArgs is also a value of type args_type -> Customer. That is what a "Customer Factory" is. Dependancy Injection is "taking objects via parameters, not newing them", you don't need a "DI container" to do it.
    – Caleth
    Oct 3, 2017 at 12:49
  • We don't need to but we do, at least according to our application sizes we have service container.
    – FZE
    Oct 3, 2017 at 12:53
3

joining late to the party!

I agree with you @FZE and you partially answer yourself in the question when you talk about a factory class.

What I tell my teams is "NEVER EVER do a new in a controller or wherever EXCEPT for inside Factory classes and unit tests".

But not for going towards "named constructors". For me having "one" constructor or "many" constructors is a small problem when compared to being able to "inject" dependencies. This is the real thing that matters.

The Pizzeria example

Say your sister buys a real pizzeria. And we model it to help in her launch.

Say we have a Pizzeria class that needs one or more Oven items to cook pizzas.

Imagine that by design the Pizzeria does not "come with ovens" but all the oposite: When you purchase the pizzeria it was announced that "it has no ovens". The Pizzeria is therefore created with zero ovens and then itself needs to increase or decrease the number of ovens as needed.

You could even say that the Oven cannot exist by itself unless it is an ElectricalOven or a FireOven. We could make then Oven an abstract class that cannot exist by itself.

You see your sister

First off your sister tells you "tomorrow I will bring in some cables, metals and buttons and I'll try to build my oven". You tell her "crazy!!". How do we instantiate the ovens? Never doing a new Oven. In fact you do not "build" an oven in your pizzeria by yourself!!!

Next day you find your sister in the door of the pizzeria shouting out loud "hey friends!! give me an oven!!" - Wow she got mad!!! You don't have a "magic global function" out there there. You just don't go out the pizzeria and shout out loud "hey friends, give me an oven!!". That's not the way! That is what a global function would look like! Ugly.

Third day... you find your sister in front a small altar with a few candles and you hear her "praying"... "Oh please, God of Ovens, bring me an oven". - Definitvely, she is for the hospital. You tell her "there's not such a thing like God of Ovens" and she cleverly answers "I do know! I'm not silly. I know there's not a God of Ovens". So? you ask. She says "I was praying to the Concept Oven" (static methods of the class Oven - very bad thing!!).

After all this madness, you call her...

What you tell to your sister

Hey, sister... What you have do is to call your machinery provider (if you buy all your things to the same company). Or maybe (depending on the specificity or your partners), you maybe have specifically an oven provider.

More over... imagine that the pizzeria belongs to a franchise. You do not have even to worry to find "oven providers". Just the franchise network will tell you "Hey, if you need ovens, just call Helena at this number and order one".

Helena here is your oven provider.

See the similitude: The franchise network at the end is who shapes your Pizzeria by giving you "hints" on "who to talk for specific things" and you don't mind if the person changes, as the protocols have been designed well. Same way an application "configures the dependencies" of a given well-designed model dependant on interfaces only.

The good thing about this is that you don't have to care about the power in watts or if it is to be electrical or fire. You just tell Helena "hey, Helena, please serve me a small oven only for weekends, for capacity of 5 parallel pizzas" or "hey, Helena, please send me an oven for 15 parallel pizzas for daily work".

Provided that all them are "ovens" and conform to the "oven standard", Helena will get you not only an oven properly initialized, but also an oven "of the proper type".

So, in the end:

This is the only solution that makes the pizzeria work well:

  1. Do not try to build your own oven (better get an oven from someone that knows how to build ones. Ie. Don't do a "new".
  2. Do not try to call the open world "hey give me an oven" (don't call a global function)
  3. ** I love this one ** Do not "pray to the concept oven" to give you an oven (don't call static methods on the Oven)
  4. DO get an oven provider from somewhere (most probably in your constructor) and then call your oven provider to give you one.

If your provider does only do "ovens and only ovens" you might want to call it oven factory ;)

Conclusion

Most people say to change this:

use Pizza\Oven\Electrical as ElectricalOven;

class Pizzeria
{
    private array $ovens = [];

    public function increaseCapacity( int $numberOfPizzas, Planning $planning ) : void
    {
        $watts = $this->someWeirdCalculator( $numberOfPizzas, $planning );
        $this->ovens[] = new ElectricalOven( $watts );
    }

    private function someWeirdCalculator( int $numberOfPizzas, Planning $planning ) : int
    {
        // do stuff
        return $watts;
    }
}

into this:

use Pizza\Oven\Electrical as ElectricalOven;

class Pizzeria
{
    private array $ovens = [];

    public function increaseCapacity( int $numberOfPizzas, Planning $planning ) : void
    {
        $this->ovens[] = ElectricalOven::fromPlanning( $numberOfPizzas, $planning );
    }
}

But this is still heavily coupled to the ElectricalOven and this is very ugly and bad. The coupling is because to use static methods, even if you don't have to have an object you still refer to that particular class.

Factory fans that don't mind about hard or loose coupling are going to do a mistake here newing the factory. They would say to move to something like the following code. To follow the example instead of Factory I'll call it Provider. I don't mind the name for this example, choose the name that better reflects your domain. They will say "hey, now the provider can give you both ElectricalOvens and FireOvens.

use Pizza\OvenProvider\Helena as OvenProvider;

class Pizzeria
{
    private array $ovens = [];

    public function increaseCapacity( int $numberOfPizzas, Planning $planning )
    {
        $ovenProvider = new OvenProvider()
        $this->ovens[] = $ovenProvider->fromPlanning( $numberOfPizzas, $planning );
    }
}

This still has a hard problem: It you make a deal with Helena when you launch your pizzeria, it's like marrying her. You will always depend on the ovens Helena can provide you. What if 5 years later you discover there's a new provider? Should you "redesign your pizzeria and deploy it again"? no!!! You should be able to change it!!!

I always push all the news to factories gotten via Dependency Injection and inject an Interface not a class name. Here OvenProvider would be an interface, not a class:

use Pizza\OvenProvider\OvenProviderInterface as OvenProvider;

class Pizzeria
{
    private array $ovens;
    private OvenProvider $ovenProvider;

    public function __construct( OvenProvider $ovenProvider )
    {
         $this->ovens = [];
         $this->ovenProvider = $ovenProvider;
    }

    public function increaseCapacity( int $numberOfPizzas, Planning $planning )
    {
        $this->ovens[] = $this->ovenProvider->fromPlanning( $numberOfPizzas, $planning );
    }
}

At every run you can even set the dependencies depending on Env Vars. For example you could build a docker image and run this instance with provider A that gives you ovens type A1 and A2 and there run another instance of the same binary that works with provider B that gives you bigger more powerful ovens type B1, B2 and B3. No need to recompile your Pizzeria class. It just works with "zero" dependendencies provided that both A and B are implementing the interface OvenProvider.

Coding overhead

The coding overhead is zero. See the Pizzeria class got a new line in the constructor to store the given factory or provider. Just a simple $this->myNiceService = $myNiceInjectedService.

From the factory point of view, the only "complex thing" is to convert "number of pizzas and Planning" into the watts to create the oven and this function was already written in the first ugle example. So no "extra" coding, just moving code around.

That is...

Let the Oven class deal with things like heatUp( int $degrees ), coolDown( int $degrees ) and things like those.

And let Helena "calculate of what kind of oven do I need and its power and so" because she is the expert.

Never forget

And to do never forget this, let me finish with an image. You know: an image is worth a thousand words, no?

This

Car factory

is not the same than this

Car

And if they are not, they deserve different classes.

0

In PHP we don't have multiple constructors per class but that won't help anyway because they would be named the same: __construct, a term that is not from the ubiquitous language.

But why do we use a constructor anyway? We use it to correctly initiate an object to a valid initial state. We can do that by making the properties private, which also helps in protecting the object encapsulation. This means that they can be changed only from the context of the class. The problem is that there could be more than one valid initial state for many of our objects. So, in order to allow only initial valid states and to protect encapsulation we can use class static methods as object constructors, one for each use case.

In C++ there are friend classes that may access private properties and so they can have a friend class as factory for each use case but we don't so we must use static methods.

BTW, using static methods doesn't break encapsulation because the context is class. You can see that by the fact that PHP let you access private properties and methods also from static class methods.

5
  • I aggree most of your explanations but there something needs to be fixed. First of all you cannot access class properties or methods from static context because static has nothing to do with object it's a shared global state it cannot call local states because it doesn't aware but you can call global state from local state. You can only call static methods properties and constants from static context. Another thing is static is a violation of encapsulation because it doesn't belong to object we cannot even mention encapsulation.
    – FZE
    Oct 4, 2017 at 6:12
  • @FZE Thanks! But yes you can access them from static context, but not using $this. Regarding encapsulation, I don't agree again, static context participate in defining encapsulation also. Static/class context is still inside encapsulation, as long as the object modified is owned/created by static context, like our case. Oct 4, 2017 at 6:27
  • I really couldn't understand do you mean like the following example ? <?php class A { private static $x = 'X'; private $y = 'Y'; public static function m() { return self::$x . self::$y; } } echo A::m(); If it is the case no you cannot. It is also meaningless because there is no object instantinated so it cannot access to variable $y if it can access that would be an ultimate violation of encapsulation :)
    – FZE
    Oct 4, 2017 at 11:18
  • Besides that a Class and an Object are different as we deal. encapsulation it's meaningful when there is an object. Static methods are nothing to do with the object it is not the object method it's global belongs to class. I created following example to show what is my point when saying it violates encapsulation. gist.github.com/feyyazesat/d0c0839df11b001d7144616d2aa68abf
    – FZE
    Oct 4, 2017 at 11:32
  • @FZE "Encapsulation is used to hide the values or state of a structured data object inside a class" - read carefully that and notice the term "class"; your example is not right, I didn't speak about that situation. See my response here: gist.github.com/xprt64/14638467d02f59bbb4127f4415c5017c Oct 4, 2017 at 12:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.