PHP in Action

Public constructors considered harmful

dagfinn | 02 May, 2008 23:05

Everybody who writes object-oriented code knows about constructors. You need them so the program knows how to instantiate objects, right? And you especially need them when a lot of things have to be done while instantiating an object. And personally, I've never considered visibility restrictions important enough to be a major argument against those languages that have lacked them (PHP 4). So why would I be skeptical of public constructors?

I got the idea after reading Joshua Kerievsky's book Refactoring to Patterns. One of his refactorings is called Replace Constructors with Creation Methods. In Java, unlike PHP, you can have multiple constructors that are distinguished only by the number and type of arguments. That may be practical sometimes, but as Kerievsky's example shows, it be more readable to have creation methods with different names instead. Which is what you have to anyway in PHP.

But why not take this one step further and do it even when there is only one constructor? For example, here's an ultra-basic Redirect class:

class Redirect {
    private $url;
 
    public function __construct($url) {
        $this->url = $url;
    }
 
    public function execute() {
        header("Location: ".$this->url);
        exit;
    }
}
 

This may seem simplistic, but it is the kind of class I might actually use at an early stage in the development of a web application. Anyway, we can apply what is essentially the same process as Kerievsky's by adding a creation method whose name is somewhat more telling than __construct().

class Redirect {
    private $url;
 
    public function __construct($url) {
        $this->url = $url;
    }
 
    public static function withUrl($url) {
        return new self($url);
    }
 
    public function execute() {
        header("Location: ".$this->url);
        exit;
    }
}
 

Now all that remains is to make the constructor private.

    private function __construct($url) {
        $this->url = $url;
    }
 

Is this an improvement? The additional code might be considered unnecessary complexity, or in a word: clutter. On the other hand, our client code might seem more readable when it looks like this:

    $redirect = Redirect::withUrl("http://www.example.com/index.php");
    $redirect->execute();
 

Instead of:

    $redirect = new Redirect("http://www.example.com/index.php");
    $redirect->execute();
 

You could object that it's fairly obvious anyway in this example that we're inputting a URL. But if the URL string is supplied as a variable or method call instead of a plain string, it would be less obvious.

No doubt there are many cases in which a plain constructor is the best choice, but I think this is worth considering.

Share and Enjoy:These icons link to social bookmarking sites where readers can share and discover new web pages.
    blogmarks del.icio.us digg NewsVine Reddit

Comments

Re: Public constructors considered harmful

Glen | 03/05/2008, 06:03

Agreed. This makes it much easier to add in debugging code, or logging, or whatever. One change, multiple additions!

Re: Public constructors considered harmful

Ben Ramsey | 05/05/2008, 09:30

I understand your argument for readability and maintenance, but I fail to see how a public constructor could be "considered harmful." "Harmful" is such a strong term, and I was anticipating from your post title that you would be talking about some unknown security vulnerability. :-)

Re: Public constructors considered harmful

Jared | 05/05/2008, 09:31

Using static method is just as bad. Using Redirect::withUrl() fixes the implementation you have to include. Factories, Locators, or Dependency Injectors are far better.

Re: Public constructors considered harmful

Joshua E Cook | 05/05/2008, 10:16

The GoF text calls this the “Factory Method” pattern. I think it’s a useful technique for when a previously-initialized instance might be returned, such as a pool of objects where the new operator is obviously wrong. It can also make the code’s intent clearer when there are several different ways to initialize a new instance. I have also seen it used in the context of an “Abstract Factory,” where the object returned may be of a concrete subclass of the (possibly abstract) class that defines the method.

Re: Public constructors considered harmful

Federico | 05/05/2008, 12:31

I'm not sure that test-infected programmers will consider this. Private methods are evil, and thy never write what they cannot test.

Re: Public constructors considered harmful

Federico | 05/05/2008, 12:33

I'm not sure that test-infected programmers will consider this. Private methods are evil, and thy never write what they cannot test.

Re: Public constructors considered harmful

Marques Johansson | 05/05/2008, 13:45

This method could prevent having to modify dependent classes in some cases where the actual constructor needs to be modified. You could have also said: public static function withUrl($url) { return self($url); } In my code I tend to have a Page class with a static Redirect function. Seems pretty readable to me.

Re: Public constructors considered harmful

dagfinn | 05/05/2008, 15:26

Mixed grill of comments to the comments: Sure, "harmful" is a bit of a strong word, but it appears to have drawn attention. I've never had so many comments before. And insightful ones, too. I am a test infected programmer, and I'm OK with testing the constructor indirectly through the creation method. And yes, Factories, dependency injection are excellent, but sometimes it needs to be simpler.

Re: Public constructors considered harmful

philippe | 06/05/2008, 06:17

using static factories helps me to manage caching/pooling features and more. Basically something like that:
public static function getInstance($id){
global $cache;

if($cache->contains($id)){
$a=$cache->get($id);
}else{
$a=new myClass($id);
$cache->add($a);
}

return $a;
}

Re: Public constructors considered harmful

Matthew | 06/05/2008, 07:07

This is not something I've thought about before, but it's an interesting idea. It also has the benefit on attaching more meaning to the argument that needs to be passed.

Two quick nitpicks:
* header("Location: $url"); should be header("Location: {$this->url}");
* You can do away with __CLASS__ by just using self (at least on PHP5). i.e. return new self($url)

(I'm sure the formatting will be a mess, but I can't find a link to a list of valid markup)

Re: Public constructors considered harmful

Ben Ramsey | 06/05/2008, 07:09

@dagfinn: While I understand that using the word "harmful" is a good attention-getter, what does it teach PHP programmers who are new to OOP? I don't think an argument can be made here for using static factory methods over constructors as being a "best practice." It's really an application architecture decision. The truth is: well-documented code can solve this problem, and using something like PHPDocumentor with PHP docblocks can provide clarity for your entire team with regard to the parameter list for a public constructor. Using the static factory method is definitely a great tip and can improve readability, but we shouldn't mislead programmers into thinking public constructors are harmful. :-)

Re: Public constructors considered harmful

dagfinn | 06/05/2008, 07:47

dagfinn

@Ben: I appreciate your point, but I'm not trying to teach basics. I'm thinking out loud about something that the gurus appear not to have considered so far. (I haven't seen it discussed before, anyway.) Maybe I should have made that clearer. It still seems to me that an intention-revealing creation method (it's not strictly a factory method) is superior to a plain constructor. I share Jared's skeptical attitude to static methods in general, but plain constructors have the same problem, since the client code has no instance avilable that can be replaced with one belonging to another class (as in Abstract Factory). As for documentation, my view is that method naming is more important and robust, because it's much harder for it to get out of sync with what's happening in the code.

Re: Public constructors considered harmful

dagfinn | 06/05/2008, 11:09

dagfinn

@Matthew: I fixed your nitpicks. I thought I could implement it untested, but I couldn't. Why am I not surprised? ;-)

Re: Public constructors considered harmful

niKo | 06/05/2008, 11:20

I can't resist it'd be a bit better like this Redirect::withUrl("http://www.example.com/index.php")->execute();

Named constructors

Mark Shapiro | 06/05/2008, 12:55

Delphi (ObjectPascal) doesn't use the 'new' syntax at all, though it does have 'constructors'. In a Delphi class, the default constructor is always named Create, but you can have other constructors with other names - by convention, these usually begin with the word Create, so you would have something like:

(in the class definition:)
constructor CreateWithURL( url: String );

(in the calling code:)
redirection := Redirection.CreateWithURL( "http://....");

The fact that CreateWithURL is defined as a constructor rather than a class (static) method changes the semantics of when/how it can be called, and what happens when it is called.

I like this style of constructors because it retains the constructor semantics while giving you the self-documenting benefits of creation methods.

Of course, with named constructors, the normal 'new' syntax would have to be changed to support identifying which constructor you want to call using something other than parameter types, and you'd need some syntax to identify that a method is a named constructor rather than some other type of method.

Re: Public constructors considered harmful

Michael Muryn | 12/05/2008, 11:30

My first comment would be that a static method named "withUrl" is maybe not significative (as static methods are not used only for creation). I would suggest to always use something that explains that the method will create an instance like Mark Shapiro explained with his Delphi example (e.g. create*).

Of course, someone that want to put the emphasis on the "new" keyword might want to do a pattern that support code like new Redirect("with_url", "http://www.example.com/index.php"); -- but that is only another way to do a same thing and might have the disadvantage of being less obvious, less separated, etc. (but now we enter into more subjectivity!)

All in all, naming a constructor when it has parameters might be more significative (and having a default [public] constructor with no data might be OK -- as a lot of case are like this, so making it private might not be OK, unless you want to explicitly do static "create" method for each of your class!).

My second comment to niKo: ... or if OO is not important... redirect_url("http://www.example.com/index.php");

 
Accessible and Valid XHTML 1.0 Strict and CSS
Powered by LifeType - Design by BalearWeb