Login with OpenID

Taming Getters and Setters

Written by Hector Virgen
Published on April 26, 2010
Last updated on April 26, 2010

It has recently come to my attention that getters and setters are evil. No, they won't run off with your first-born child, nor will they pour gasoline into your cereal. But, according to other programmers on the interwebs, they can lead to poor object-oriented design.

However, problems can be found with just about any single line of code in your application. As a PHP developer I've even heard complaints about the opening PHP tag itself!

But I want to look more closely at getters and setters to find out why they are evil and what we can do to tame them.

Based on various web blogs, getters are referred to as a "code smell" that can easily cause problems down the road. Most of these articles are referring to Java, but since I program in PHP I wanted to see if it applies. Let's use a simple User object to see what havoc it can wreak:

class User
{
    protected $_firstName;
    protected $_lastName;

    public function getFirstName()
    {
        return $this->_firstName;
    }

    public function getLastName()
    {
        return $this->_lastName;
    }
}

So here we have a simple User class that appears to politely return the first and last name of the user on demand. I don't see any horns, hooves, nor a tail, so how can this be evil?

What we mere mortals tend to forget is that evil is the master of deception. It won't become obvious that we've raised hell in code by committing this innocent-looking class to our repository.

However, let's see what happens when we start using this class. Let's say we're building a simple profile page and need to display the user's full name wrapped in a fancy H1:

$user = new User();
echo "<h1>{$user->getFirstName()} {$user->getLastName()}</h1>";

Seems simple enough. Just get the first and last name and put a space in between. You test it, everything works, you can commit your code and go home for the day.

But the problem here is that your view is determining which data constitutes the user's full name. And this becomes a bigger problem when you have many views all trying to show the same full name.

Since this is really a business rule, it belongs in the User class. Behold:

class User
{
    protected $_firstName;
    protected $_lastName;

    public function getFullName()
    {
        return "{$this->_firstName} {$this->_lastName}";
    }
}

With this small change to our User class, we can guarantee that the full name will always be rendered the same. In other words, we don't need no stinking getters.

So we've successfully converted our evil class into something slightly less evil. We've removed those pesky getters and encapsulated the full name in a method. But we still have a long way to go before we can sleep peacefully.

By now you may be asking yourself "how does the first and last name properties get populated?" That's a very good question!

If you've been following my blog you may have noticed that I am a fan of the Data Mapper pattern. It separates business logic from persistence logic, allowing you to swap out persistence logic at any time. This means in order to persist that user object, the mapper needs to somehow access the $_firstName and $_lastName properties.

Can this be done without getters? Sure, why not?

class User
{
    /* ... */

    public function getData()
    {
        return array(
            'firstName' => $this->_firstName,
            'lastName' => $this->_lastName
        );
    }
}

But is this any better? Now it's just a "super-getter" that returns all the properties at once. So this doesn't really solve the problem, it just disguises it under a different method. So you might as well keep all those getters in there because your mapper is going to need to access them anyways.

What about setters?

Another problem you may run into is when a user decides to change her name (maybe she just got married and wants to update her last name). How would you accomplish this without a setter?

What you might want to do is avoid using a set* method. Maybe we'll call it "updateLastName":

class User
{
    /* ... */

    public function updateLastName($newLastName)
    {
        $this->_lastName = $newLastName;
    }
}

In reality, this method is identical to what a setLastName method would be. So how is this any better?

It's not. Especially when you consider how the mapper is supposed to load a user from persistent storage. For example, it will need to create a new User object and then "set" all of its properties based on the persisted data. In other words, you're going to need setters.

Not-So-Conclusive Conclusion

When it comes to domain entities, like the aforementioned User object, some parts of your application are going to need the getters and setters, while other parts should be working with just the "business" methods.

In other words, getters and setters are a necessary evil that helps you accomplish certain tasks, but they must be used wisely.

Some OO purists may say that getters and setters are a "code smell", but I say that using only getters and setters is where the real stink is.

Comments

blog comments powered by Disqus