Mixing Static and Object Methods – Calling Statically from an Object Instance

So, I finally got down to cobbling together subviews into views. The code’s not working, but an earlier design decision to make the entire ArticleView class static led me down this weird path.

I’ll use some fake code examples.

The code’s a lot shorter if I use an object to hold the context.

     $context = ...; // code to prepare the context goes here
     $files->saveHtml( Views::html( $context ));
     // the alternative is
     $views = new View( $article );
     $files->saveHtml( $views->html() );

So, it’s a little shorter.  The method names and class names I used were much longer, so the effect was even more pronounced.

So, I added a some code to make it OO.

    function __construct( $article ) {
         $this->context = ... // code to prepare context

Then I altered the methods I called from the controller so they were also OO.

    public function html() {
        $out = self::articleSubView( $this->context['article'] );

I found that all my subviews were still static.

Static is Good

Static methods have a nice quality: they’re basically stateless.  They have only local variables, or should.

That means they’re also easy to test.

Also, static calls to static methods: they don’t alter the object state, and they don’t depend on the object state.

A class that’s mostly static methods is also easier to test: there’s less code with state.  Stateful code is harder to test because each method call could change the state. For example, the order of the calls could matter.

However, static methods are hard to mock, for unit tests.  That means you can mock only the public object methods.  Maybe all calls from the outside world should be on objects, and only internal calls should be static.  That would preserve encapsulation.

public function foo() { ... self::bar(); ... }
private static function bar() { ... }

Ehh… that looks like overkill.  It only matters if someone makes a call to the static class from outside the class.

I’m starting to wonder if I stumbled on a useful pattern.

Oddities and Random, Unordered Thoughts

SO says “NO,” private methods are not to be tested.  I disagree with that. I’d rather test a  class by testing each method, rather than testing it through a few public methods.  The alternative is to break out the methods into a public, static class, and test that — but that’s introducing more classes for the sake of testing.  I would rather break the tests into two sets: tests of the public methods, and tests of the private methods.  When a test of a private method will prevent refactoring, remove it.

Here are some discussions:

https://henrikwarne.com/2014/02/09/unit-testing-private-methods/

http://www.peterprovost.org/blog/2012/05/31/my-take-on-unit-testing-private-methods/

I like Provost’s suggestion to comment the code.  Why do more work than necessary?  I still don’t agree that only public methods should be tested, just because 🙂


The code will look odd, because the static methods take arguments that, normally, would just be object properties.

On the other hand, you have a clean separation when you assign local variables in the arguments:

function foo($a, $b, $c) {
    code using $a $b $c
}
function bar() {
    self::foo($this->get('a'), 'blah', $this->c);
}

In the OO version,  you might put some noise like this in the foo function:

$a = $this->get('a');
$b = 'blah';
$c = $this->c;

And, again, scalars are copied by value, so… you don’t mutate the values.


Static methods should NOT USE STATIC CLASS PROPERTIES.


Moving a method out of a class, into a Trait, or some utility class, would be trivial.

Of course, object properties are really just global to the object.  Globals are “bad” 🙂 So, passing them as arguments should be a “good” thing.


Think about it – if you have an object with 50 properties and 1,000 lines, is that good?

Of course not. The normal thing to do is to do additional object decomposition so you have smaller classes and fewer properties per object.  The trade off is that, as the code generally becomes easier to read, the application gets a little bigger, and tracing through the code is harder.

However, if you refactor it using static methods, maybe you can retain the code length, but eliminate many properties.  You could also refactor into more classes, if it made sense.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s