When to use static vs instantiated classes

PhpOopClass

Php Problem Overview


PHP is my first programming language. I can't quite wrap my head around when to use static classes vs instantiated objects.

I realize that you can duplicate and clone objects. However in all of my time using php any object or function always ended up as a single return (array, string, int) value or void.

I understand concepts in books like a video game character class. duplicate car object and make the new one red, that all makes sense, but what doesn't is its application in php and web apps.

A simple example. A blog. What objects of a blog would be best implemented as static or instantiated objects? The DB class? Why not just instantiate the db object in the global scope? Why not make every object static instead? What about performance?

Is it all just style? Is there a proper way to do this stuff?

Php Solutions


Solution 1 - Php

This is quite an interesting question -- and answers might get interesting too ^^

The simplest way to consider things might be :

  • use an instanciated class where each object has data on its own (like a user has a name)
  • use a static class when it's just a tool that works on other stuff (like, for instance, a syntax converter for BB code to HTML ; it doesn't have a life on its own)

(Yeah, I admit, really really overly-simplified...)

One thing about static methods/classes is that they don't facilitate unit testing (at least in PHP, but probably in other languages too).

Another thing about static data is that only one instance of it exists in your program : if you set MyClass::$myData to some value somewhere, it'll have this value, and only it, every where -- Speaking about the user, you would be able to have only one user -- which is not that great, is it ?

For a blog system, what could I say ? There's not much I would write as static, actually, I think ; maybe the DB-access class, but probably not, in the end ^^

Solution 2 - Php

The main two reasons against using static methods are:

  • code using static methods is hard to test
  • code using static methods is hard to extend

Having a static method call inside some other method is actually worse than importing a global variable. In PHP, classes are global symbols, so every time you call a static method you rely on a global symbol (the class name). This is a case when global is evil. I had problems with this kind of approach with some component of Zend Framework. There are classes which use static method calls (factories) in order to build objects. It was impossible for me to supply another factory to that instance in order to get a customized object returned. The solution to this problem is to only use instances and instace methods and enforce singletons and the like in the beginning of the program.

Miško Hevery, who works as an Agile Coach at Google, has an interesting theory, or rather advise, that we should separate the object creation time from the time we use the object. So the life cycle of a program is split in two. The first part (the main() method let's say), which takes care of all the object wiring in your application and the part that does the actual work.

So instead of having:

class HttpClient
{
    public function request()
    {
        return HttpResponse::build();
    }
}

We should rather do:

class HttpClient
{
    private $httpResponseFactory;

    public function __construct($httpResponseFactory)
    {
        $this->httpResponseFactory = $httpResponseFactory;
    }

    public function request()
    {
        return $this->httpResponseFactory->build();
    }
}

And then, in the index/main page, we'd do (this is the object wiring step, or the time to create the graph of instances to be used by the program):

$httpResponseFactory = new HttpResponseFactory;
$httpClient          = new HttpClient($httpResponseFactory);
$httpResponse        = $httpClient->request();

The main idea is to decouple the dependencies out of your classes. This way the code is much more extensible and, the most important part for me, testable. Why is it more important to be testable? Because I don't always write library code, so extensibility is not that important, but testability is important when I do refactoring. Anyways, testable code usually yields extensible code, so it's not really an either-or situation.

Miško Hevery also makes a clear distinction between singletons and Singletons (with or without a capital S). The difference is very simple. Singletons with a lower case "s" are enforced by the wiring in the index/main. You instantiate an object of a class which does not implement the Singleton pattern and take care that you only pass that instance to any other instance which needs it. On the other hand, Singleton, with a capital "S" is an implementation of the classical (anti-)pattern. Basically a global in disguise which does not have much use in the PHP world. I haven't seen one up to this point. If you want a single DB connection to be used by all your classes is better to do it like this:

$db = new DbConnection;

$users    = new UserCollection($db);
$posts    = new PostCollection($db);
$comments = new CommentsCollection($db);

By doing the above it's clear that we have a singleton and we also have a nice way to inject a mock or a stub in our tests. It's surprisingly how unit tests lead to a better design. But it makes lots of sense when you think that tests force you to think about the way you'd use that code.

/**
 * An example of a test using PHPUnit. The point is to see how easy it is to
 * pass the UserCollection constructor an alternative implementation of
 * DbCollection.
 */
class UserCollection extends PHPUnit_Framework_TestCase
{
    public function testGetAllComments()
    {
        $mockedMethods = array('query');
        $dbMock = $this->getMock('DbConnection', $mockedMethods);
        $dbMock->expects($this->any())
               ->method('query')
               ->will($this->returnValue(array('John', 'George')));

        $userCollection = new UserCollection($dbMock);
        $allUsers       = $userCollection->getAll();

        $this->assertEquals(array('John', 'George'), $allUsers);
    }
}

The only situation where I'd use (and I've used them to mimic the JavaScript prototype object in PHP 5.3) static members is when I know that the respective field will have the same value cross-instance. At that point you can use a static property and maybe a pair of static getter/setter methods. Anyway, don't forget to add possibility for overriding the static member with an instance member. For example Zend Framework was using a static property in order to specify the name of the DB adapter class used in instances of Zend_Db_Table. It's been awhile since I've used them so it may no longer be relevant, but that's how I remember it.

Static methods that don't deal with static properties should be functions. PHP has functions and we should use them.

Solution 3 - Php

So in PHP static can be applied to functions or variables. Non-static variables are tied to a specific instance of a class. Non-static methods act on an instance of a class. So let's make up a class called BlogPost.

title would be a non-static member. It contains the title of that blog post. We might also have a method called find_related(). It's not static because it requires information from a specific instance of the blog post class.

This class would look something like this:

class blog_post {
    public $title;
    public $my_dao;

    public function find_related() {
        $this->my_dao->find_all_with_title_words($this->title);
    }
}

On the other hand, using static functions, you might write a class like this:

class blog_post_helper {
    public static function find_related($blog_post) {
         // Do stuff.
    }
}

In this case, since the function is static and isn't acting on any particular blog post, you must pass in the blog post as an argument.

Fundamentally this is a question about object oriented design. Your classes are the nouns in your system, and the functions that act on them are the verbs. Static functions are procedural. You pass in the object of the functions as arguments.


Update: I'd also add that the decision is rarely between instance methods and static methods, and more between using classes and using associative arrays. For example, in a blogging app, you either read blog posts from the database and convert them into objects, or you leave them in the result set and treat them as associative arrays. Then you write functions that take associative arrays or lists of associative arrays as arguments.

In the OO scenario, you write methods on your BlogPost class that act on individual posts, and you write static methods that act on collections of posts.

Solution 4 - Php

> Is it all just style?

A long way, yes. You can write perfectly good object oriented programs without ever using static members. In fact some people would argue that static members are an impurity in the first place. I would suggest that - as a beginner in oop - you try to avoid static members all together. It will force you in the direction of writing in an object oriented rather than procedural style.

Solution 5 - Php

I have a different approach to most answers here, especially when using PHP. I think all classes should be static unless you have a good reason why not. Some of the "why not" reasons are:

  • You need multiple instances of the class
  • Your class needs to be extended
  • Parts of your code cannot share the class variables with any other parts

Let me take one example. Since every PHP script produces HTML code, my framework has an html writer class. This ensures that no other class will attempt to write HTML as it is a specialised task that should be concentrated into a single class.

Typically, you would use the html class like this:

html::set_attribute('class','myclass');
html::tag('div');
$str=html::get_buffer();

Every time get_buffer() is called, it resets everything so that the next class to use the html writer starts in a known state.

All my static classes have an init() function which needs to be called before the class is used for the first time. This is more by convention than a necessity.

The alternative to a static class in this case is messy. You would not want every class that needs to write a tiny bit of html to have to manage an instance of the html writer.

Now I will give you an example of when not to use static classes. My form class manages a list of form elements like text inputs, dropdown lists and more. It is typically used like this:

$form = new form(stuff here);
$form->add(new text(stuff here));
$form->add(new submit(stuff here));
$form->render(); // Creates the entire form using the html class

There is no way you could do this with static classes, especially considering that some of the constructors of each added class do a lot of work. Also, the chain of inheritance for all the elements is quite complex. So this is a clear example where static classes should not be used.

Most utility classes such as the ones converting/formatting strings are good candidates for being a static class. My rule is simple: everything goes static in PHP unless there is one reason why it should not.

Solution 6 - Php

"Having a static method call inside some other method is actually worse than importing a global variable." (define "worse")... and "Static methods that don't deal with static properties should be functions".

These are both pretty sweeping statements. If I have a set of functions that are related in subject matter, but instance data is totally inappropriate, I would much rather have them defined in a class and not each of them in the global namespace. I'm just using the mechanics available in PHP5 to

  • give them all a namespace -- avoiding any name clashes
  • keep them physically located together instead of becoming scattered across a project -- other developers can more easily find what's already available and are less likely to re-invent the wheel
  • let me use class consts instead of global defines for any magic values

it's just altogether a convenient way to enforce higher cohesion and lower coupling.

And FWIW -- there is no such thing, at least in PHP5, as "static classes"; methods and properties can be static. To prevent instantiation of the class, one can declare it abstract, also.

Solution 7 - Php

First ask yourself, what is this object going to represent? An object instance is good for operating on separate sets of dynamic data.

A good example would be ORM or database abstraction layer. You may have multiple database connections.

$db1 = new Db(array('host' => $host1, 'username' => $username1, 'password' => $password1));
$db2 = new Db(array('host' => $host2, 'username' => $username2, 'password' => $password2));

Those two connections can now operate independently:

$someRecordsFromDb1 = $db1->getRows($selectStatement);
$someRecordsFromDb2 = $db2->getRows($selectStatement);

Now within this package/library, there may be other classes such as Db_Row, etc. to represent a specific row returned from a SELECT statement. If this Db_Row class was a static class, then that would be assuming you only have one row of data in one database and it would be impossible to do what an object instance could. With an instance, you can now have an unlimited number of rows in an unlimited number of tables in an unlimited number of databases. The only limit is the server hardware ;).

For example, if the getRows method on the Db object returns an array of Db_Row objects, you can now operate on each row independently of one another:

foreach ($someRecordsFromDb1 as $row) {
    // change some values
    $row->someFieldValue = 'I am the value for someFieldValue';
    $row->anotherDbField = 1;
    
    // now save that record/row
    $row->save();
}

foreach ($someRecordsFromDb2 as $row) {
    // delete a row
    $row->delete();
}

A good example of a static class would be something that handles registry variables, or session variables since there will only be one registry or one session per user.

In one part of your application:

Session::set('someVar', 'toThisValue');

And in another part:

Session::get('someVar'); // returns 'toThisValue'

Since there is only ever going to be one user at a time per session, there is no point in creating an instance for the Session.

I hope this helps, along with the other answers to help clear things up. As a side note, check out "cohesion" and "coupling". They outline some very, very good practices to use when writing your code that apply to all programming languages.

Solution 8 - Php

If your class is static that means you can't pass its object around to other classes (since there is no instance possible) so that means all your classes will be directly using that static class which means your code is now tightly coupled with the class.

Tight coupling makes your code less reusable, fragile and prone to bugs. You want to avoid static classes to be able to pass instance of the class to other classes.

And yes this is only one of many other reasons some of which have been already mentioned.

Solution 9 - Php

In general, you should use member variables and member functions unless it absolutely has to be shared between all instances or unless you are creating a singleton. Using member data and member functions allows you to reuse your functions for multiple different pieces of data, whereas you can only have one copy of data on which you operate if you use static data and functions. Additionally, though not as applicable to PHP, static functions and data lead to code being non-reentrant, whereas class data facilitates reentrancy.

Solution 10 - Php

I'd like to say that there is definitely a case where I'd like static variables- in cross-language applications. You could have a class that you pass a language to (e.g. $_SESSION['language']) and it in turn accesses other classes that are designed like so:

Srings.php //The main class to access
StringsENUS.php  //English/US 
StringsESAR.php  //Spanish/Argentina
//...etc

Using Strings::getString("somestring") is a nice way to abstract your language usage out of your application. You could do it however you please but in this case having each strings file have constants with string values that are accessed by the Strings class works pretty well.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser73119View Question on Stackoverflow
Solution 1 - PhpPascal MARTINView Answer on Stackoverflow
Solution 2 - PhpIonuț G. StanView Answer on Stackoverflow
Solution 3 - PhpRafeView Answer on Stackoverflow
Solution 4 - PhptroelsknView Answer on Stackoverflow
Solution 5 - PhpJG EstiotView Answer on Stackoverflow
Solution 6 - PhpgrantwparksView Answer on Stackoverflow
Solution 7 - PhpTresView Answer on Stackoverflow
Solution 8 - PhpMuhammad Hasan KhanView Answer on Stackoverflow
Solution 9 - PhpMichael Aaron SafyanView Answer on Stackoverflow
Solution 10 - PhpdudewadView Answer on Stackoverflow