Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OOP - using private properties and public getter

Tags:

oop

php

Just a simple question. I've seen many cases where the code is implemented like the following:

class User
{
    private $id;
    private $firstname;
    private $lastname;

    public function __construct() { some code }

    public function getId() { return $this->id; }

    public function getFirstname() { return $this->firstname; }

    public function setFirstname($value) { $this->firstname = $value; }
}

// And properties are accessed like:
$user->getFirstname();
$user->getId();

So what is the reason of using private properties and having public getters, instead of making the properties public and accessing them directly like:

$user->firstname;

PS: Is it OK if I use the second method?

EDIT

Seems like I did not research well before asking this question (guess I used wrong keys to search on the topic). Here is another good answer to (almost) the same question: https://stackoverflow.com/a/1568230/1075534

Basically, for my case, one good reason to use getters and setters is to avoid changing 250 classes that directly access the property. So for example, imagine that I was not using a getter:

class User
{
    public $firstname = 'abc';
    public $lastname  = 'cde';
}

$user = new User(); 

echo $user->firstname . ' ' . $user->lastname;

Now, imagine that I want to change the behavior of my app and I decide to print names as capitalized. In this case then I would search each implementation (250 in this case :) and capitalize the output wherever I called the properties. But, rather If I used a getter then I would just change the getter method:

class User
{
    private $firstname = 'abc';
    private $lastname  = 'def';

    public getFirstname()
    {
        return ucfirst(strtolower($this->firstname));
    }

    public getLastname()
    {
        return  ucfirst(strtolower($this->lastname));
    }
} 

Also, bear in mind that a getter might not only gather information from a single property. Imagine the following:

class User
{
    private $firstname = 'abc';
    private $lastname  = 'def';

    public function getName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}

For those who still have questions on this matter, I suggest them reading the material that Gordon provided and especially the answer that I've linked.

like image 519
Savas Vedova Avatar asked Jun 21 '13 09:06

Savas Vedova


2 Answers

Using Accessors satisfies the Uniform Access Principle.

Quoting Wikipedia:

The Uniform Access Principle was put forth by Bertrand Meyer. It states "All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation." This principle applies generally to object-oriented programming languages. In simpler form, it states that there should be no difference between working with an attribute, precomputed property, or method/query.

In his blog, Fowler explains

It effectively means that a client of the person should neither know nor care whether the age is stored or computed. This gives the person object the flexibility to change between the two easily as well as removing what is usually an unnecessary concern from the client. It's an important part of encapsulation - or at least the data-hiding aspect of encapsulation.

To illustrate the point, imagine a class like this:

class Now
{
    public $timestamp;

    public function getTomorrow()
    {
        return strtotime('+1 day', $this->timestamp);
    }

 // … more code

When you do it this way, you are forcing the developer using that class to know the implementation details. To get the now's timestamp, the developer has to do

echo $now->timestamp;

whereas to get the computed timestamp for tomorrow, the developer has to do

echo $now->getTomorrow();

But the developer shouldn't need to care, so if you want to follow the UAP, you will provide an Accessor to get the timestamp. And funnel all access through methods.

Doing so also has the additional benefit of a more stable API. Imagine you need to change the implementation detail later in the project and turn timestamp into a DateTime object. Your class now looks like this:

class Now
{
    public $dateTime; // <-- no longer just a timestamp

    public function getTomorrow()
    {
        return strtotime('+1 day', $this->dateTime->getTimestamp());
    }

 // … more code

Now any developer that previously used $now->timestamp will have to change their consuming code to accomodate for that change. Whereas when you had used a Getter right from the start, it wouldn't be a problem at all, because the Getter would have made sure that it returned the timestamp. To further prove that point, note how the developer would not have to change anything to consume getTomorrow(). Although internally we changed the details, the public API still behaves the same.

Note that UAP is just a guideline. Modern IDEs make it dirt easy to generate Accessors and Mutators for you, so it's easy to follow. But it's not an absolute truth. If you can reasonably justify not following it, then don't follow it and use public properties. But it should be an informed decision.

However, in general, you want to avoid Getters (and Setters) anyway and just tell your objects what to do. This will give a much slimmer API. If your API has lots and lots of Getters, chances are you are scattering code that really should be inside the object into the consumers.

like image 52
Gordon Avatar answered Nov 16 '22 14:11

Gordon


When a variable is declared public, it is accessible from any class making it easier for you as a programmer to use; thus explaining your desire to use the second method you described. However, some variables need to be declared private for security reasons (also it is sometimes seen as a good practice). The fact that we declare them privatemake them only accessible in their own class. If You however need to use them in a function in another class then you need to follow the first method you described .

like image 2
user2497624 Avatar answered Nov 16 '22 14:11

user2497624