DEV Community

Static classes are evil

wrong-about-everything on December 20, 2017

In spite of some languages, e.g., PHP, don’t have such thing as static classes, the concept is still present there. Class consisting entirely of st...
Collapse
 
kspeakman profile image
Kasey Speakman • Edited

I think the article is quite correct. But there is a critical unstated assumption: that static classes operate on a shared mutable state. The correct use of static methods are as deterministic functions. For example String.Join(",", strings) is probably deterministic in a given language, making it easily testable and understandable.

Collapse
 
wrongabouteverything profile image
wrong-about-everything

there is a critical unstated assumption: that static classes operate on a shared mutable state

There's no such assumption. Every issue I've listed holds true without it. And concerning your example, here is how I would write it:


interface String
{
    public function value();
}

class DefaultString implements String
{
    private $string;

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

    public function value()
    {
        return $this->string;
    }
}

class Joined implements String
{
    /**
     * @var String[]
     */
    private $strings;
    private $delimeter;

    public function __construct(array $strings, $delimeter)
    {
        $this->strings = $strings;
        $this->delimeter = $delimeter;
    }

    public function value()
    {
        return
            implode(
                $this->delimeter,
                array_map(
                    function (String $s) {
                        return $s->value();
                    },
                    $this->strings
                )
            );
    }
}

class UpperCase implements String
{
    private $string;

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

    public function value()
    {
        return strtoupper($this->string->value());
    }
}

var_dump(
    (new Joined(
        [
            new UpperCase(new DefaultString('harry')),
            new UpperCase(new DefaultString('potter')),
        ],
        ' '
    ))
        ->value()
);

It looks way more declarative, human-readable, composable and reusable.

Collapse
 
kspeakman profile image
Kasey Speakman

There's no such assumption. Every issue I've listed holds true without it.

Eh, not really. I mean I get it... statics are incompatible with OOP or "objects all the way down" philosophy. Right on. But they have valid uses which remain testable and maintainable, albeit not following the OOP philosophy.

Anyway, not meant to argue your main point. I think you are right. Just that there are nuances to every tool. Best wishes!

Collapse
 
mindplay profile image
Rasmus Schultz

You can think of a static class as a pseudo-namespace for functions. If your functions are free of state, side-effects and dependencies, there's absolutely nothing wrong with that - for one, functions are generally easier to write tests for.

Static classes in PHP are necessary, if you want to use functions, because literal functions can't be auto-loaded.

As long as you code according to the limitations of a real namespace, those classes are effectively just namespaces that can autoload.

That said, if I have functions in a library that someone might need to override or extend in a project, I go for a stateless service class instead, and sometimes an interface for abstraction - which enables dependency injection, so that someone can replace the service with their own project-specific implementation.

For example, something like "count unique occurrences of x in y" is a good candidate for something you'd never need/want to override, while something like "calculate shipping tax" is much more likely to have a project-specific definition.

Being unable to replace functions is the only "evil" thing about static classes, assuming it abides by the other mentioned constraints.

Collapse
 
wrongabouteverything profile image
wrong-about-everything

Yes, I agree, static classes are absolutely fine -- in procedural programming, but not in OOP.

Collapse
 
mindplay profile image
Rasmus Schultz

So your argument is everything should be OOP? No one should use functions?

Thread Thread
 
wrongabouteverything profile image
wrong-about-everything

No, I'm just saying that static classes have nothing to do with OOP.

Thread Thread
 
mindplay profile image
Rasmus Schultz

I think that's what I'm saying, by describing it as a pseudo-namespace.

But what's your point?

Because it sounds like you're trying to build an argument. Or perhaps you're just trying to clarify the fact that it's not automatically OO just because you use the class keyword?

The interface/class example wrapping a string made it sound dangerously like you think that would actually be meaningful somehow - I'm afraid you might mislead the people you're trying explain the difference to, towards actually programming like that.

Thread Thread
 
wrongabouteverything profile image
wrong-about-everything

My point is that static classes should not be used in OOP.

That example with decorated Strings is an OOP alternative to procedural StringUtils class.

Thread Thread
 
mindplay profile image
Rasmus Schultz

It's okay to call functions though, like how your string example calls strtoupper and several others - but it's not okay to write/call your own functions?

I understand it's "not OOP", but "should not be used"?

I think there are well founded reasons why most languages support both paradigms - there are cases for functions and cases for classes, I think, and your string example is sufficiently complex and verbose as to leave me pretty firmly convinced of that.

Collapse
 
scottshipp profile image
scottshipp • Edited

Static methods can also be difficult to test, if they make use of dependencies that are environment-specific.

I would like to point out that the term "static class" has a specific, different meaning in Java. Java developers will usually assume that you are talking about a static nested class if you use that term.

Nice article!

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Ah. In C#, static is a modifier you can put when declaring a class (top-level or nested). All it does is compile-time verify that all the class members are also static. Otherwise you will get an error. It doesn't have any particular usage implications that I'm aware of.