Use of static methods within an object?

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Use of static methods within an object?

Post by Jenk »

Stumbled upon the thought of using a static method in place of an object method, for functions that do not require access to object properties; and example:

Code: Select all

class Locator
{
    private static function _formatName($name)
    {
        $name = ucwords(str_replace('_', ' ', $name));
        return str_replace(' ', '/', $name);
    }
    
    public function loadClass($name)
    {
        $name = self::_formatName($name); // instead of $this->_formatName()
        if (!is_file($name) || !is_readable($name))
        {
            throw new Locator_Exception(
                'class file "' . $name . '" does not exist, or cannot be read'
            );
        }
        else
        {
            include_once $name;
        }
    }
}

?>
The theory being that the object is not 'weighed down' with methods that do not need access to the objects live properties, if you understand what I mean..

In the above example, it propbably makes negligible difference, but I'm sure there will be circumstances where it could make a lot of difference.

What do you guys think of this practice?
User avatar
theYinYeti
Forum Newbie
Posts: 15
Joined: Thu Oct 26, 2006 3:33 pm
Location: France

Post by theYinYeti »

I tend to agree, although there may be exceptions.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

You're really going to see minimal overhead differences for doing this. I do it on occassion but be careful not to scatter this around too much because unravelling it all again when you need something a bit more stateful is going to be a pain. If you're calling a method as part of the object then the fact it calls another method in the *already instantiated* object is not really any different to what you're doing with a static.
User avatar
johno
Forum Commoner
Posts: 36
Joined: Fri May 05, 2006 6:54 am
Location: Bratislava/Slovakia
Contact:

Post by johno »

If there is a method that don't needs to know about object properties its a bad smell. But it could be just a simple helper method like in this case. I would just make it a private (nonstatic) method and factor that out if some duplication in different classes arises. I have no idea why it would be better to make it static. Can you?

Static keyword is IMHO a sign that something is not in right place.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

johno wrote:If there is a method that don't needs to know about object properties its a bad smell.
That's entirely subjective. I commonly place a static factory method in one of my central classes in a system to make retreiving other objects easier. It's not bothered what the rest of the class does but I certainly wouldn't call it a smell. Unless of course that's the sort of thing you meant by a helper.
User avatar
theFool
Forum Newbie
Posts: 17
Joined: Thu Oct 26, 2006 2:00 am
Location: Berlin, DE

Post by theFool »

Is there a sense in a private static method?
User avatar
johno
Forum Commoner
Posts: 36
Joined: Fri May 05, 2006 6:54 am
Location: Bratislava/Slovakia
Contact:

Post by johno »

d11wtq wrote:That's entirely subjective. I commonly place a static factory method in one of my central classes in a system to make retreiving other objects easier. It's not bothered what the rest of the class does but I certainly wouldn't call it a smell. Unless of course that's the sort of thing you meant by a helper.
You are right. Duplication is the main smell, but duplication always starts when things aren't in their right place. So this is something I recognize as a pre-smell. If there is something static it can be always refactored out. You may not need that refactor that out immediately or even ever. But as soon there starts duplication you will realize that mostly these helpers need to be refactored into separate classes.

Suppose I want to make different naming convention for classes and paths? Whats going to be refactored out? Right - formatName into separate NamingConvention class.

Static factory method is IMHO a helper.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

theFool wrote:Is there a sense in a private static method?
Yes, in that example, it's only available to the Loader class and it's objects. Just where I want it to be. It wouldn't be any different if it were not a static method, it would still be private, as it is not a part of the interface.
sike
Forum Commoner
Posts: 84
Joined: Wed Aug 02, 2006 8:33 am

Post by sike »

i tend to avoid statics whenever possible. for me they are "objectified" globals - and that smells alot (at least for my nose (: ).
additionally they are harder to test.

cheers
Chris
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Static methods aren't necessarily bad. The problem is that their global nature makes them prime candidates for overuse. A good idea is to look at how a static will be used. If its a general helper function of limited use in a narrow scope, then it's probably okay. If it's a very useful function in a wide scope then its getting decidedly smelly. Once a static proliferates through a codebase it becomes difficult to maintain loosely coupled classes - the static becomes a dependency in too many places. Multiply the number of similar statics by a few factors and you have a potential bombsite on your hands ;).

A recent example is the Zend Framework - there's a lot of backpeddling over the Zend class which offered Zend::loadClass(), Zend::dump(), and other general use statics. Since they are very useful across the whole framework, and even into the code based on the framework, it proliferated quickly. The result is a trend to refactor the statics into separate classes. For example, in the 0.20 incubator you now have Zend_Registry which should replace Zend::register() and Zend::registry(). The statics in incubator are now simple proxies using Zend_Registry for backwards compatibility (something I disagree with since the statics still exist, but won't blame folk for since it would spark a BC rebellion in the ranks of users).

My rule of thumb is to avoid statics unless they clearly add an advantage. If no apparent advantage is evident, then there's no real reason to use a static.
User avatar
theFool
Forum Newbie
Posts: 17
Joined: Thu Oct 26, 2006 2:00 am
Location: Berlin, DE

Post by theFool »

Jenk wrote: It wouldn't be any different if it were not a static method, it would still be private...
Exactly, so we have both the same thoughts but different conclusions. :D
Personally, I only would make a method static if there is a concrete reason for that. In your example, I would let that method be non-static, just because it can only be accessed by objects of that class anyway.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

The purpose really is duplication, but not in a number of lines form.. in memory form.

This can be labelled premptive optimisation, but the theory is: every object has a copy of the method, vs every object references only one instance of the method.

The reason for it being private is negligible. It's private because I don't want anything outside of that class using it, if I change it to public in my original post, it bears no difference on the topic. :)
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Post by alex.barylski »

If I remember correctly using a static method will not reduce the overhead of a function call as the 'this' pointer is the only additional overhead (de-referencing this pointer).

Static are not only used for performance, but for reason...

In some OOP languages such as C++ you can declare a method as 'const' which prevents that method from manipulating the object, but still allows retreival of data members.

Static methods are similar in nature except that they should be used when the method should never be used to manipulate or access the class (object) which it belongs to.

Sometimes a function logically belongs to a class but doesn't act specifically on that classes data, in which case a static is typically used.

For instance in MFC there is a CFile class:

http://msdn.microsoft.com/library/defau ... embers.asp

Most of the operations deal with object manipulation, but at the bottom there are some statics, mostly out of nesseccity...

They use statics in this case because...the delete() or rename() operations are very much a part of a file "class" but do not require an "object" therefore they remain as static both for performance and for function.

My point is statics are fine, like any technique as long as your can actually justify their use and the positives outweigh the negatives - giver!!! Whether they should be used as a performance boost...not unless they make sense to do so, but if they do...yes of course captialize on the performance gains (albeit as minimal as they are).

Cheers :)
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

They use statics in this case because...the delete() or rename() operations are very much a part of a file "class" but do not require an "object" therefore they remain as static both for performance and for function.
The point I would make (in PHP at least) is that using statics for performance gains is premature optimisation. In the scope of Design, all that achieves is numerous global methods and little measurable performance gain (has anyone using this actually measured the gain???). There are times to compromise between good design and optimisation but I doubt statics is one of them. I'd accept a tiny performance hit for looser coupling - most of us have accepted such hits in the past when PHP5 first came out ;).

In MFC those concerns are moot - MFC is a class library and most libraries are tightly coupled. If you look at PHP, the Zend Framework is the opposite - it's extremely loosely coupled and allowing too many statics only damages that aspect.

Note that using a static in a class, means that class must be aware of the static's class name. If we spend so much time discussing alternatives to direct instantiation and Singleton static calls like Class_Name::getInstance() peppering our class constructors (tight coupling) - like Registry, Service Locator and Factories, then we should apply the same measures where possible to another reason for class coupling - static methods.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Maugrim_The_Reaper wrote:has anyone using this actually measured the gain???
I've just done a few benchmarks and it's negligible at best. I wanted to measure the actual memory usage and time taken for just one call to the method but xdebug is whining about undefined functions on the server I have access to.

Speed comparisons calling the methods:

Code: Select all

<?php

class StaticGainsTest
{
    private function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = $this->getNull();
        }
        return $ret;
    }
}

$start = microtime(true);
$o = new StaticGainsTest();
$o->getLotsOfNull();
$end = microtime(true);

echo "Called non-static method 100000 times in ". ($end - $start) . " seconds\n";

?>

Code: Select all

Called non-static method 100000 times in 6.48325991631 seconds

Code: Select all

<?php

class StaticGainsTest
{
    private static function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = self::getNull();
        }
        return $ret;
    }
}

$start = microtime(true);
$o = new StaticGainsTest();
$o->getLotsOfNull();
$end = microtime(true);

echo "Called static method 100000 times in ". ($end - $start) . " seconds\n";

?>

Code: Select all

Called static method 100000 times in 6.25048303604 seconds


Speed comparisons creating the objects:

Code: Select all

<?php

class StaticGainsTest
{
    private function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = $this->getNull();
        }
        return $ret;
    }
}

$start = microtime(true);
$collection = array();
for ($i = 0; $i < 10000; $i++)
{
    $collection[] = new StaticGainsTest();
}
$end = microtime(true);

echo "Created a collection of 10000 objects with a no static methods in " . ($end-$start) . " seconds\n";

?>

Code: Select all

Created a collection of 10000 objects with a no static methods in 0.0555810928345 seconds

Code: Select all

<?php

class StaticGainsTest
{
    private static function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = self::getNull();
        }
        return $ret;
    }
}

$start = microtime(true);
$collection = array();
for ($i = 0; $i < 10000; $i++)
{
    $collection[] = new StaticGainsTest();
}
$end = microtime(true);

echo "Created a collection of 10000 objects with a static method in " . ($end-$start) . " seconds\n";

?>

Code: Select all

Created a collection of 10000 objects with a static method in 0.028272151947 seconds

Memory comparisons calling the methods:

Code: Select all

<?php

class StaticGainsTest
{
    private function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = $this->getNull();
        }
        return $ret;
    }
}

$o = new StaticGainsTest();
$o->getLotsOfNull();

echo "Ran non-static method 100000 times using " . (xdebug_peak_memory_usage()) . " bytes in memory\n";

?>

Code: Select all

Ran non-static method 100000 times using 6164328 bytes in memory

Code: Select all

<?php

class StaticGainsTest
{
    private static function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = self::getNull();
        }
        return $ret;
    }
}

$o = new StaticGainsTest();
$o->getLotsOfNull();

echo "Ran static method 100000 times using " . (xdebug_peak_memory_usage()) . " bytes in memory\n";

?>

Code: Select all

Ran static method 100000 times using 6164392 bytes in memory


Memory comparisons creating the objects:

Code: Select all

<?php

class StaticGainsTest
{
    private function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = $this->getNull();
        }
        return $ret;
    }
}

$collection = array();
for ($i = 0; $i < 10000; $i++)
{
    $collection[] = new StaticGainsTest();
}

echo "Created a collection of 10000 objects with a no static methods using " . (xdebug_peak_memory_usage()) . " bytes in memory\n";

?>

Code: Select all

Created a collection of 10000 objects with a no static methods using 1994504 bytes in memory

Code: Select all

<?php

class StaticGainsTest
{
    private static function getNull()
    {
        return null;
    }

    public function getLotsOfNull()
    {
        $ret = array();
        for ($i = 0; $i < 100000; $i++)
        {
            $ret[] = self::getNull();
        }
        return $ret;
    }
}

$collection = array();
for ($i = 0; $i < 10000; $i++)
{
    $collection[] = new StaticGainsTest();
}

echo "Created a collection of 10000 objects with a static method using " . (xdebug_peak_memory_usage()) . " bytes in memory\n";

?>

Code: Select all

Created a collection of 10000 objects with a static method using 1994576 bytes in memory
Post Reply