Static class - to be or not to be

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

Post Reply
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Static class - to be or not to be

Post by VladSun »

// topic split

viewtopic.php?f=50&t=117965
josh wrote:And yeah those "static" rewrites that (Vlad?) suggested destroys any shred of extensibility you had going, in my opinion. If you want I could get in to detail on why that is, but it would be best to start another thread at that point.
There are 10 types of people in this world, those who understand binary and those who don't
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Static class - to be or not to be

Post by josh »

In order to understand the problems associated with static classes, you have to talk about lots of other design forces. We know coupling should be minimized in a good design. Let's consider two examples

Example A - the static way:
[syntax]function foo() {
$service = Service::getInstance();
$service->doSomething();
}[/syntax]

In example A, if I want to change how the Service class behaves I would need to hard code my changes. Either I'd be making hard coded changes to the Service class, or hard coded changes to the code that uses the service class (because I'd subclass it, for example I'd create My_Service class which extends Service and then I'd have to update all the parts of the code that referenced the old Service class).

Example B - the extensible way:
[syntax]function foo( Service $service ) {
$service->doSomething();
}[/syntax]

In example B we've got what's called dependency injection. Instead of our method "reaching out" and coupling itself to a specific class, it asks for that class. Presumably I could have 1,000s of methods that use this Service API, and I could replace it with instances of "My_Service" simply by making one change somewhere else, because of the way type hinting works any class that extends from Service will satisfy this method's signature, without me having to physically go in and edit the method's signature. Contrast that with the static example where I am editing large(r) amounts of code in any scenario.

So both dependency injection, and low coupling - commonly accepted aspects of maintainable code, both of these are intuitively different paradigms then static or "procedural" code. The java math library is static and is still good code, but java programmers have resorted to "programming gymnastics" like using the adapter pattern to extend the functionality. People who've had to jump through hoops to write a seemingly simple plugin for some other system will feel my pain.

So as you can see a static class can be every bit as functional for a "stateless" procedure like doing math, however where static classes and objects are not equal - is in how they affect the rest of the system as a whole. If you have more specific questions I'd be glad to elaborate more. Like maybe you could write what you think the advantage of a static class is and I'll play devils advocate and try to debunk it, could open up the discussion.


Example 2A
[syntax]
$result = Math:add(1,2);
$result = Math::multiply( $result, 2 );
[/syntax]

Example 2B
[syntax]
$one = new Math_Number(1);
$two = new Math_Number(2);

$result = $one->plus($two)->times($two);
[/syntax]
You also loose "chaining" when you do your stuff as a static API. On a larger math operation it would be half as much "mental juggling" to read the code written in the fluent chaining technique.

Static classes just aren't object oriented at all. You can't call it OOP. All it is are name-spaced functions. You're just typing the extra "class" syntax to be fancy. There's no point in using a class if you're not going to take advantage of both data+state. As I showed, even something seemingly stateless like a math operation does have state. Its incredibly subjective but each method has its pros & cons. Maybe you could try to list something you think of as being "stateless" and I'll show how in my subjective opinion it does too have state.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: Static class - to be or not to be

Post by VladSun »

Nice "article", josh! I have to agree many things you said about using static class are true. Still, I will argue about some points you've made:
josh wrote:Static classes just aren't object oriented at all. You can't call it OOP. All it is are name-spaced functions. You're just typing the extra "class" syntax to be fancy. There's no point in using a class if you're not going to take advantage of both data+state. As I showed, even something seemingly stateless like a math operation does have state. Its incredibly subjective but each method has its pros & cons. Maybe you could try to list something you think of as being "stateless" and I'll show how in my subjective opinion it does too have state.
Yes, I agree that static classes are not OOP, but they can be used for *encapsulation* of a PP code. Consider having one public method and several (let's say 10) private methods used by this public method. The PP way would require 10 function declarations inside the main function. Even if you just look at its ASCII art, it will look like a class, so why not put it into a class. And as you said, till PHP 5.3.0 it will also be a "namespace".

josh wrote:

Code: Select all

function foo() {
  $service = Service::getInstance();
  $service->doSomething();
}

Code: Select all

function foo() {
  $service = ServiceFactory::create();
  $service->doSomething();
}
Now all you have to change is the class of the object returned by the ServiceFactory::create() method. Nowhere else.
The DI pattern can not target this problem.

An example:

Code: Select all

class Router
{
	public static function getPhysicalInterfaceList()
	{
		$interfaces = CommandProxy::execute('/sbin/ifconfig');
		if (preg_match_all('/(\w+\d+)\s+Link encap:Ethernet/s', $interfaces, $interfaces))
			return $interfaces[1];
		else
			return array();
	}
}

Code: Select all

class CommandProxy
{
	/**
	 *
	 * @param string $s
	 * @return string
	 */

	public static function execute($s)
	{
		return LocalCommand::execute($s);
	}
}

class RemoteCommand
{
	/**
	 *
	 * @param string $s
	 * @return string
	 */

	const IP	= '127.0.0.1';
	const USER	= 'root';
	const PROTO = 'ssh';

	public static function execute($s)
	{
		$command = self::PROTO." ".self::USER."@".self::IP." ".$s;
		return trim(`$command 2>&1`);
	}
}


class LocalCommand
{
	/**
	 *
	 * @param string $s
	 * @return string
	 */

	public static function execute($s)
	{
		return trim(`$command 2>&1`);
	}
}

class SuCommand
{
	/**
	 *
	 * @param string $s
	 * @return string
	 */

	public static function execute($s)
	{
		return trim(`sudo $command 2>&1`);
	}
}
EDIT: The RemoteCommand, LocalCommand and SuCommand don't have to be static classes...
There are 10 types of people in this world, those who understand binary and those who don't
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Static class - to be or not to be

Post by josh »

Right all it does is make your code "pretty" by grouping the functions together. Objects do a lot more besides organize your code (design patterns). Static classes provide very little hard value. I'd be interested to see if someone can come up with something that should be stateless. I don't think it can be done but maybe someone can prove me wrong. Not sure if you meant your "router" to be an example but as you pointed out most "routers" do have state. Also your static creation method makes it not a static class. You're going ->doSomething() not ::doSomething(). I wouldn't call it a static class I'd call it a violation of the "hollywood" principle.

Even when I heat up my toast in the morning my toaster has state. I can't think of any kind of "machine" or program or function that wouldn't have both state & behavior, maybe its just me. I think static classes are still useful but in a way its a needless stepping stone on the path to learning to program. Its not where you want to be in the end:

- When I started, I learned how to use functions instead of include statements
- Then I learned to group my functions in a static class and organize those classes
- Then I learned about polymorphism, and real OOP, and won't write a static class again if I can help it.

I think static classes will eventually go the way of how the tail did on humans. As we evolved we got better tools and didn't need a tail anymore, so we got rid of it (if you believe in that stuff). Even still newer paradigms like aspect oriented programming, and event driven programming will render today's OOP obsolete one day. Judging by how slowly people are adopting OOP I think we'll have OOP for a while though before the industry adopts more advanced stuff.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: Static class - to be or not to be

Post by VladSun »

The Router class is not a static one indeed. I've just shown a piece of it as a short example.

And as I said, generally I do agree with you :) I had read some books just before posting here ;)

PHP in Action
Objects, Design, Agility

DAGFINN REIERSØL
MARCUS BAKER
CHRIS SHIFLETT

3.2.2 When to use class methods
There are several uses for class methods. Some of the more common ones are
• Creation methods and factory methods
• Finder methods
• Procedural code
• Replacements for constants

Creation methods and factory methods are methods that create and return object
instances. They’re frequently used when ordinary creation using new becomes insuf-
ficient.
Finder methods—to find an object in a database or other storage—may be con-
sidered a special case of creation methods, since they return a new object instance.
Some things can be done just as effectively with a snippet of procedural code as
with an object-oriented method. Simple calculations and conversions are examples of
this. Sometimes it’s relevant to put procedural code into a class instead of using plain
functions. The reason for keeping it in a class may be to avoid name collisions with other
functions or because it belongs in class that is otherwise based on instance methods.
The fact that static methods can be used for all these things does not prove that they
should always be used. Static methods have the advantage of simplicity, but they are
hard to replace on the fly. If a method belongs to an object instance, it’s potentially
pluggable. We can replace the object instance with a different one to change the behav-
ior significantly without changing either the client code or the original class. Let’s re-
examine our earlier Finder example:
$finder = new ProductFinder;
$product = $finder->find($productCode);
If we replace the product finder with another class (for example, we might want to get
the product information from a web service instead), both the old ProductFinder
class and the second line in the example can remain the same; the finder is pluggable.
On the other hand, using the static method:
$product = Product::find($productCode);
Here, the behavior is built into the Product class, and there is no way to change it
without changing that line of code. That’s not much of a problem if it occurs only
once, but if the class name is used repeatedly, it’s another matter.
This problem may become particularly acute in unit testing: we may want to
replace the find() method with another, fake one, that returns fixed test data instead
of actual data from the database.
I know you're a strict TDD developer, so I'm sure you won't ever use static classes (even a single static method) ;)
There are 10 types of people in this world, those who understand binary and those who don't
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Static class - to be or not to be

Post by josh »

I don't even like to use constants, for the same reasons. I prefer a public method to a private method. A lot of times I'll write a class and make heavy use of public & private. Then during re-factoring, I will find a new home for all the private methods, so I can get them "out of the way" but also bring them into my public API, test them as they evolve to be more complex. What starts out as a constant eventually turns into a 1,000 lines of code when the customers are done with ya. lol

I do have lots of static stuff in my own code bases. But I recognize that as one of my flaws as a programmer, and I am constantly trying to better myself.

Its not just for testing either. Maybe you want the fake finder for a test? Maybe the client wants an RSS finder? You should just be able to change code just to change it, because often you'll end up needing to. When I write an "export" I get a flury of emails saying thats cool, can you make it export into "xyz format"? A successful application is going to have a LOT of growing pains. A few hours spent doing it right the first time pays off when you find out its limiting your sales a year later.

The way I look at it, if its well tested, its good code. Static code causes a problem in unit testing because its inherently bad code. Anything that causes pains in your tests, will eventually cause you bigger pains with your clients. The tests are not causing the pains the bad code is ;-) You know in an MMorpg as you walk around parts of the "map" light up as you discover new areas in the game? Its kind of like that. The tests are just illuminating problems that already existed. Its letting you see the problems, stare the beasts in the face and giving you the opportunity to sleigh them now. So whether or not you do unit testing you really show use discretion with the "static" stuff.
Post Reply